虚拟机内搭建ltp-server步骤
LTP是哈工大社会计算与信息检索研究中心研发的系列中文自然语言处理工具,可以由网络服务的形式进行使用,也就是ltp-server。
本文使用的ltp源码与模型版本都是3.4.0。
ltp模型下载
linux版源码下载
安装ltp步骤参考文档
cmake源码
虚拟机配置
(我用的是win10,ltp-server只能在Linux上运行,所以只能安装虚拟机,Linux用户可跳过此步骤)
我使用的是VirtualBox-6.0.14,可能与旧版的有所区别,网上搜到很多端口配置方案都不能正常工作,以下是我成功的配置步骤。
虚拟机安装
安装虚拟机与centos的步骤略过,相关教程很多。需要注意两点:
1.虚拟机的内存设定至少为4GB,推荐5GB。刚启动ltp-server时,内存占用3.23GB。实际使用过程中发现请求量大时,内存占用会更高。
2.在安装centos时选择“最小安装”,默认是带图形化界面的,会占用更多内存。
网络配置
1.VirtualBox的管理器中,管理->全局设定->网络->"+“号(添加新NAT网络)->端口转发
2.添加如下图的网络转发规则,图中的IP和端口都不能为空。9876是我设定的ltp-server端口。22是远程连接的端口。
主机IP | 主机端口 | 子系统IP | 子系统端口 |
---|---|---|---|
127.0.0.1 | 22 | 10.0.2.15 | 22 |
127.0.0.1 | 9876 | 10.0.2.15 | 9876 |
3.配置虚拟机的网络,点击虚拟机的设置->网络,选择连接方式为“NAT网络”和界面名称myNetwork。
4.启动虚拟机,修改防火墙设置。
开放端口:firewall-cmd --add-port=9876/tcp --zone=public --permanent
生效:firewall-cmd --reload
查看开放的端口:firewall-cmd --zone=public --list-ports
ltp-server安装
1.复制ltp3.4.0源码、模型数据到centos中。(路径可在后面配置)
2.复制cmake源码到centos中。
3.安装cmake所需的环境:
yum install gcc,gcc-c++,make,libssl-dev,openssl-devel
4.在cmake源码目录下,依次进行:
./configure->gmake->make install
5.在ltp源码目录下,依次进行:
./configure->make
6.在ltp同目录下创建ltp_server.sh文件:
data_dir=/home/lx/ltp_data/
lex_dir="/home/lx/ltp_data/"
cws_path="${data_dir}cws.model"
cws_lex="${lex_dir}cws.lex"
pos_path="${data_dir}pos.model"
pos_lex="${lex_dir}pos.lex"
ner_path="${data_dir}ner.model"
parser_path="${data_dir}parser.model"
srl_path="${data_dir}pisrl.model"
nohup ltp-3.4.0/bin/ltp_server --threads 4 --port 9876 --log-level 2 --segmentor-model $cws_path --segmentor-lexicon $cws_path --postagger-model $pos_path --postagger-lexicon $pos_lex --ner-model $ner_path --parser-model $parser_path --srl-model $srl_path > ltp.log 2>&1 &
其中data_dir是模型的存放路径,lex_dir是用户词典路径。启动参数的设置参考ltp-server。
如果是复制sh文件到centos中,注意修改文件的可执行权限。
7.运行ltp_server.sh。运行日志都会写入“ltp.log”中。
8.运行htop(一个监控内存和进程的程序)查看内存使用情况。
ltp-client使用
一个python3.6版的调用ltp-server模块,仅供参考:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
import socket
from urllib.error import HTTPError, URLError
from urllib import request, parse
from common.log_manager import getLog
from common.config_manager import get_config_values
from runtime.arclist import ArcListByClient
from runtime.rolelist import SrlListWithClient
logger = getLog()
'''
参考NLP:http://ltp.readthedocs.io/zh_CN/latest/ltpserver.html
搭载NLP在服务器上,开放指定的端口
提供一个免费使用的中文分词,词性标注,命名实体识别,语义角色标注,依存语法分析的接口
'''
class LTP_CLIENT():
def __init__(self, server_url="http://127.0.0.1:9876/ltp"):
# task 任务的具体形式,可以是分词:'ws',词性标注:'pos',依存语法分析:'dp',命名实体识别:ner语义角色标注:'srl',或者全部:'all'
self.server_url = get_config_values("ltp", "server_url")
def input_json(self, sentence_list, task, word_lists, pos_lists):
'''
功能:根据输入列表,构建输入的xml的具体形式
input_list是列表时,采用xml,否则不用xml
'''
try:
if isinstance(sentence_list, list):
ss_start = '<?xml version="1.0" encoding="utf-8" ?><xml4nlp><note sent="y" word="n" pos="n" ne="n" parser="n" srl="n" /><doc><para>'
sent_node_list_str = ''
for i, sent in enumerate(sentence_list):
sent_start = '<sent cont="%s">' % sent.replace('"', '“')
word_node_list_str = ''
if word_lists is not None:
for j, word in enumerate(word_lists[i]):
pos_str = ''
if pos_lists is not None:
pos_str = ' pos="%s"' % pos_lists[i][j]
word_node_str = '<word cont="%s"%s />' % (word, pos_str)
word_node_list_str += word_node_str
sent_node_str = sent_start + word_node_list_str + ' </sent>'
sent_node_list_str += sent_node_str
ss_end = '</para></doc></xml4nlp>'
ss = ss_start + sent_node_list_str + ss_end
data = {'s': ss, 'x': 'y', 't': task}
else:
data = {'s': sentence_list, 'x': 'n', 't': task}
except:
logger.exception('构造client中的查询xml时出错,<%s><%s><%s>' % (sentence_list, word_lists, pos_lists))
data = None
return data
def output_json(self, input_json):
'''
功能:根据输入的xml,上传服务器,返回指定任务的结果json对象
'''
try:
req = request.Request(self.server_url)
params = bytes(parse.urlencode(input_json), encoding='utf8')
response = request.urlopen(req, params, timeout=5)
content = response.read().strip().strip(b'\x00')
return json.loads(content)
except HTTPError as e:
if 'Bad Gateway' == e.msg:
# 部署到nginx时,ltp_server服务未开启的报错
logger.warn('ltp_server连接失败:%s' % self.server_url)
except socket.timeout as et:
logger.warn('连接ltp_server超时')
except URLError as e:
if '10061' in str(e.reason):
# 作为独立服务时,ltp_server服务未开启的报错
# [由于目标计算机积极拒绝,无法连接。]
logger.warn('ltp_server未启动:%s' % self.server_url)
except:
logger.exception('从ltp_server获取数据出错:%s' % self.server_url)
return None
def test_working(self):
if self.get_segments(['测试']) is None:
logger.warn('ltp_server连通测试失败')
return False
else:
logger.info('ltp_server连通测试通过')
return True
def get_ltp_result(self, sentence_list, task, word_lists=None, pos_lists=None):
input_json = self.input_json(sentence_list, task, word_lists, pos_lists)
if input_json is None:
return None
result = self.output_json(input_json)
return result
def get_segments(self, sentence_list):
'''
功能:实现分词文本的分词
返回值:每个文本的形成一个列表[['word1','word2'],['word1','word3'],……]
'''
task = 'ws'
content = self.get_ltp_result(sentence_list, task)
if content is None:
logger.warn('分词结果为空,输入句为:%s' % sentence_list)
return None
segmented_text_list = []
for text_other in content[0]:
sent = text_other
text = []
for word in sent:
text.append(word['cont'])
segmented_text_list.append(text)
return segmented_text_list
def get_postags(self, sentence_list, word_lists=None):
'''
功能:实现文本中每个词的词性标注
返回值:每个文本是一个列表,列表中的每个词也是个列表[[['word1',u'O'],['word2',u'O']],[['word2',u'O'],['word5',u'O']],……]
'''
task = 'pos'
content = self.get_ltp_result(sentence_list, task, word_lists)
if content is None:
return None, None
word_lists = []
pos_lists = []
for text_other in content[0]:
sent = text_other
word_list = []
pos_list = []
for word in sent:
word_list.append(word['cont'])
pos_list.append(word['pos'])
word_lists.append(word_list)
pos_lists.append(pos_list)
return word_lists, pos_lists
def get_arcs(self, sentence_list):
'''
# head = parent+1
# relation = relate 可以从中间抽取head 和 relation 构成NLP 的标准输出,但是为了根据自己的情况,直接输出返回的全部的信息
功能:实现依存句法分析
返回值:每个文本的形成一个列表
[[{u'relate': u'WP', u'cont': u'\uff0c', u'id': 4, u'parent': 3, u'pos': u'wp'},{u'relate': u'RAD', u'cont': u'\u7684', u'id': 1, u'parent': 0, u'pos': u'u'}],……]
'''
task = 'dp'
content = self.get_ltp_result(sentence_list, task)
arc_list = []
for text_other in content[0]:
sent = text_other
syntaxparser_text = []
for word in sent:
syntaxparser_text.append(word)
arc_list.append(ArcListByClient(syntaxparser_text))
return arc_list
def get_nets(self, sentence_list, word_lists=None, pos_lists=None):
'''
功能:识别文本中的命名实体:地名,组织名和机构名
参数repead:表示是否进行去重处理 ,默认是不去重
参数Entity_dist:表示每个文本,返回的识别后的列表,还是抽取后的实体字典,默认返回的是列表
返回值的形式:1.[[['word1',u'O'],['word2',u'O'],['word3',u'O']],[['word2',u'O'],['word3',u'O'],['word4',u'O']],……]
2.[{'person':[],'place':[],'organization':[]},{'person':[],'place':[],'organization':[]},{'person':[],'place':[],'organization':[]},……]
'''
task = 'ner'
content = self.get_ltp_result(sentence_list, task, word_lists, pos_lists)
entity_text_list = []
word_lists = []
pos_lists = []
net_lists = []
for text_other in content[0]:
sent = text_other
# text = []
word_list = []
pos_list = []
net_list = []
for word in sent:
word_list.append(word['cont'])
pos_list.append(word['pos'])
net_list.append(word['ne'])
# text.append([word['cont'], word['ne']])
# entity_text_list.append(text)
word_lists.append(word_list)
pos_lists.append(pos_list)
net_lists.append(net_list)
return word_lists, pos_lists, net_lists
def get_srls(self, sentence_list, word_lists=None):
'''
功能:语义角色标注
返回值:文本中存在角色的每个词的具体的标记列表
词:[u'\u662f', [{u'type': u'A0', u'end': 1, u'beg': 0, u'id': 0}]]
'''
task = 'srl'
content = self.get_ltp_result(sentence_list, task)
if content is None:
return None
srl_lists = []
for text_other in content[0]:
sent = text_other
rolelabeller_text = []
for word in sent:
if word['arg'] != []:
rolelabeller_text.append([word['cont'], word['arg']])
srl_list = SrlListWithClient(rolelabeller_text)
srl_lists.append(srl_list.get_label_strs())
return srl_lists
if __name__ == '__main__':
import time
intput_list = ['各单位要结合新分解的指标计划,调整营销策略,细化工作措施,抓住剩下的***个半月时间,鼓足干劲、扎实工作,努力完成各项目标任务。',
'一要建立市场开拓工作机制市场开拓不是一个部门、或者一部分人的事,公司一切生产经营活动的核心都是为了发展市场、增加效益,营销、生产、基建、规划、调度等多个部门、不同专业要密切配合、高效联动,努力形成推动市场发展的整体合力,形成“大市场、大营销、大服务”的工作格局。',
'中国,是以华夏文明为源泉、中华文化为基础,并以汉族为主体民族的多民族国家,通用汉语、汉字,汉族与少数民族被统称为“中华民族”,又自称为炎黄子孙、龙的传人'
]
client = LTP_CLIENT()
# print(model.get_segments(intput_list))
t0 = time.time()
for s in intput_list:
print(client.get_nets(s))
t1 = time.time()
print(t1 - t0, len(s))
其中引用缺少的模块(对语义角色标注和依存句法进行了二次封装)是在业务中用到的其他功能,不会影响对ltp的调用。调用ltp-server的url为:
http://127.0.0.1:9876/ltp