上一篇博文说了整个工程的数据整理,我们这里再来说一下如何进行语义的理解,如何理解用户所要问的问题。这里我们运用的是类似一个正则匹配的规则匹配来解析我们输入的 query,这里我拍脑袋列出了城市类的知识图谱的一些常见的提问方式,如下图所示:
这里我列出了8大类城市类的提问方法,比如第一类的问题表示什么城市有哪些景点,一般都是两种的提问方式:城市 有什么 景点,有什么 景点在 城市。我们可以用一些常见的表达来替换 景点 和 城市。这里我们需要提取城市名的实体并且和提取景点这个词可以替换的词,那么我们可以先定义这些可替换的词和几个常见的实体类。
pos_city = "city"
pos_culture = "cul"
pos_scene = "scene"
pos_food = "food"
city_entity = (WordReplace(pos=pos_city))
culture_entity = (WordReplace(pos=pos_culture))
scene_entity = (WordReplace(pos=pos_scene))
food_entity = (WordReplace(pos=pos_food))
city = (WordReplace("城市") | WordReplace("地方") | WordReplace("位置") | WordReplace("地点") | WordReplace("哪儿") | WordReplace("哪") | WordReplace("哪里"))
food = (WordReplace("吃的") | WordReplace("小吃") | WordReplace("美食") | WordReplace("好吃的") | WordReplace("特产"))
culture = (WordReplace("风俗") | WordReplace("文化") | WordReplace("人文") | WordReplace("风情") | WordReplace("特色") | WordReplace("风土人情"))
scene = (WordReplace("景点") | WordReplace("美景") | WordReplace("风景") | WordReplace("好玩"))
introduce = (WordReplace("介绍") | WordReplace("简介") | WordReplace("描述") | WordReplace("了解"))
这里我们可以看到 pos_ 表示每个类别的词性,_entity 表示这个类别的实体,因为后续我们会用到 jieba 分词,这些实体名会通过导入外部词典来进行匹配,city、food、culture、scene 和 introduce分别表示城市、美食、文化、景点和介绍可替换的一些表达。比如说“哈尔滨有什么好玩的地方”,经过 jieba 分词之后就可以得到我们需要的实体和意图了,分词的结果如下图所示:
可以看到这里哈尔滨匹配上了 city 这个词性属于 city_entity,地方也匹配上了city这个可替换的说法。有了这些可替换的词语之后,我们可以开始写规则,按照第一类话术,我们可以将规则写成:
Rule(condition_num=1, condition=((city_entity + Star(Any(), greedy=False) + scene + Star(Any(), greedy=False)) |
(scene + Star(Any(), greedy=False) + city_entity + Star(Any(), greedy=False))),
action=Question.city_question_search)
city_entity 表示城市列表中有的所有城市,Star(Any(), greedy=False) 表示任意字符串,scene 表示景点的配置说法,city_question_search 表示以城市为关键字进行搜索。通过这种方式,我们就可以将 广州有哪些景点 | 有哪些景点在广州 这类话术匹配上。
我们有了城市名之后,就可以去ES进行城市名的关键词搜索,这里我们看一下以城市名进行 elasticsearch 搜索的方法:
@staticmethod
def city_question_search(word_objects):
query = None
for w in word_objects:
if w.pos == pos_city:
query = {
"query": {
"bool": {
"must": [
{
"query_string": {
"default_field": "city",
"query": w.token
}
}
]
}
},
"size": 100
}
break
return query
这里的 word_objects 表示一个 word 的词和标注,因为我们通过外部词汇导入,城市名的词性会是 city,最后会返回 elasticsearch 的搜索 json 语句。 有了搜索语句之后,我们就可以将这个 json 搜索语句拿到 elasticsearch 中去进行搜索,最后我们进行 elasticsearch 的结果解析就可以得到问题的答案,方法如下所示:
def query_search(self, es, query):
return es.search(index='城市信息', body=query)
def get_es_result(self, condition_num, result):
values = []
# 广州有什么景点 | 有哪些景点是广州的
if condition_num == 1:
for hit in result['hits']['hits']:
value = hit['_source']['scenery']
values = value.split(',')
# 成都有哪些美食 | 有哪些美食是成都的
elif condition_num == 2:
for hit in result['hits']['hits']:
value = hit['_source']['foods']
values = value.split(',')
# 乌鲁木齐有哪些风俗 | 哪些风俗是乌鲁木齐的
elif condition_num == 3:
for hit in result['hits']['hits']:
value = hit['_source']['cultures']
values = value.split(',')
# 臭豆腐是哪个城市的小吃| 哪个城市有串串香
elif condition_num == 4:
for hit in result['hits']['hits']:
value = hit['_source']['city']
values.append(value)
# 介绍一下深圳 | 给我深圳的介绍
elif condition_num == 5:
for hit in result['hits']['hits']:
value = hit['_source']['describe']
values.append(value)
# 哈尔滨是一个什么样的城市
elif condition_num == 6:
for hit in result['hits']['hits']:
value = hit['_source']['describe']
values.append(value)
# 天安门在哪儿 | 哪儿有天安门
elif condition_num == 7:
for hit in result['hits']['hits']:
value = hit['_source']['city']
values.append(value)
# 花鼓灯是哪儿的 | 哪儿有花鼓灯
elif condition_num == 8:
for hit in result['hits']['hits']:
value = hit['_source']['city']
values.append(value)
return list(set(values))
以 “哈尔滨有什么好玩的地方” 为例,我们可以从elasticsearch 中搜索到结果,然后获取 scenery 字段的结果就可以了。
所以整个城市类知识图谱的搭建流程如下图所示:
接着我们可以将这个工程用 Django 部署到服务器上,首先我们创建一个Django 工程,然后在 views.py 文件中创建一个 cityFaqService 方法:
@csrf_exempt
def cityFaqService(request):
es = Elasticsearch(['192.168.1.118:9200'])
city_faq = CityFAQ(es)
reqBody = request.body
if reqBody:
try:
jsondata = json.loads(reqBody.decode('utf-8'))
query = jsondata["query"]
information = city_faq.city_faq_search(query=query)
print(information)
if information == "我不晓得你在说啥子...":
responseData = {
"result": "我不晓得你在说啥子..."
}
else:
responseData = {
"result": ",".join(information).strip()
}
return JsonResponse(responseData)
except Exception as e:
# print(">>>>>>>>>>", e)
return JsonResponse({"result": "error"})
这是输出的地方,然后我们入口是 urls.py 文件中:
from django.urls import path
from .views import cityFaqService
urlpatterns = [
path('city_faq', cityFaqService),
]
最后我们在8001端口启动服务:
python manage.py runserver 0.0.0.0:8001
我们可以在 postman 中进行测试了,我随机测几条,效果都还不错:
从上数的结果看来,基本上城市类的问题,这个知识图谱系统都能够进行正确的回答,效果还是不错的,希望这两篇博文能够帮助大家对知识图谱的理解和搭建有个深度的理解和帮助,谢谢大家,文中如有纰漏,也望大家不吝指教。代码详见 GitHub 。