Elastic进阶教程:构建一个基于NLP的财经热点分析系统

2022-11-21 10:41:49 浏览数 (1)

随着 8.0 的发布,现在我们能够将 PyTorch 机器学习模型上传到 Elasticsearch 中,以在 Elastic Stack 中提供现代自然语言处理 (NLP) 的能力。现在,Elasticsearch 用户能够集成用于构建 NLP 模型的最流行格式之一,并将这些模型作为 NLP 数据管道的一部分通过我们的推理处理器整合到 Elasticsearch 中。添加 PyTorch 模型以及新的 ANN 搜索 API的能力为Elastic Enterprise Search添加了一个全新的向量(双关语)。这使得,直接在Elasticsearch实现基于倒排索引的相关性搜索与基于向量的语义搜索的结合成为可能。

我们在上一篇文章《Elastic 进阶教程:在Elasticsearch中部署中文NER模型》中介绍了如何部署NER模型,在文末,我们提到

“而当下,像搜索深度理解,智能推荐等需要更为精准的搜索的场景,和NLP的结合已经成为必然。而在Elasticsearch中直接实现NLP,将帮助我们以极简的架构、极低的成本,极快的速度去上线一个包含了NLP功能的搜索项目”。在本文中,我们将展示一个简单的案例,通过将命名实体识别模型嵌入到企业搜索的解决方案当中,实现一个财经热点分析系统。

整个案例将分成以下几个部分:

  • 采集财经信息
  • 采集数据的存储和处理
  • 数据查看
  • 数据热点分析
  • 使用图查询了解数据关系

收集财经信息

对于财经信息的采集,在企业搜索的解决方案中我们可以通过多种方式来采集数据。包括,网络爬虫,文件上传,与API索引。

企业搜索的数据索引方式企业搜索的数据索引方式

在上一个案例《Elasticsearch进阶教程:轻松构造一个全方位的信息检索系统》,我们已经介绍过如何使用爬虫采集数据。在这个案例中,我们将采用API的方式,写入财经数据。

选择数据源

第一步,我们需要找到一个包含财经信息的数据源。我找了很多网站,但很难找到免费且质量高的资源。以前挖地兔还能免费获得财经信息。现在变成挖地兔pro之后,虽然还号称免费,但现在的积分模式,获取财经新闻所需要5000点的积分几乎等于收费。

当然,我们的目的是以演示为主,现在不用太过于纠结于数据的质量,先把框架和基础搭起来再去寻找更好的数据源会是一个更敏捷的路径。这里,我选择了天行数据——其包含了160多个免费接口,会员用户一键调用,接口统一,上手简单。而且除了财经数据,我们还可以结合时政、热点,一起进行分析:

对于免费用户来说,每种类型的免费数据,一天分别有100次的接口免费调用:

对于热点分析而非实实时响应的需求来说,已经足够了。

调用的接口如上图所示,非常简单,只需要一个apikey,以及每页返回的数据的条目num。而apikey从控制台获取:

比如一段简单的Python代码:

代码语言:python代码运行次数:0复制
caijing_news_url = "http://api.tianapi.com/caijing/index?key=" APIKey "&num=50"
payload={}
headers = {}
response = requests.request("GET", caijing_news_url, headers=headers, data=payload)

结果:

数据定期采集

因为我们的目标是定制一个热点大屏,而查看的频率是按小时计的,最划算的方式是通过serverless函数,配置每天的6点、12点、18点采集数据:

数据处理

我们可以将数据分成两个部分,一个是原始数据的查看,一个是数据的统计分析(热点大屏)。对于原始数据,我们可以通过App search进行处理。而对于数据的统计分析,我们可以通过NER(命名实体识别)功能,提取出实体,对实体进行丰富和统计

数据存储

从天行数据采集来的原始数据我们可以存储在App search的引擎中。

可以通过API的方式进行数据的上传:

我们点击从API索引之后,会弹出一个包含示例的窗口,该窗口展示了如何以curl命令进行数据上传,其中包含了对应api的详细URI,以及用户认证所需要的信息。

App search为我们简化了接口,我们不再需要对索引进行各种预处理,包括mapping, settings的设置。也不需要学习如何使用es的bulk api。我们只需要记住自己创建的对应的engine名称:news

按照以上接口的例子, 可以很快变成一段Python代码,与上文从天行数据读取新闻的代码一起,读取数据之后,写入App sesarch:

代码语言:python代码运行次数:0复制
    url_list=[
    "http://api.tianapi.com/guonei/index?key=" APIKey "&num=50",
    "http://api.tianapi.com/caijing/index?key=" APIKey "&num=50"
    ]
    appsearch_url="https://lex-demo.ent.ap-east-1.aws.elastic-cloud.com/api/as/v1/engines/news/documents"
    app_search_headers = {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer private-95cprqnzy9j4f3onvgkddwx1'
    }    
    for url in url_list:
        response = requests.request("GET", url, headers=headers, data=payload)
        result = response.json()
        if result['code'] == 200:
            newslist = result['newslist']
            response = requests.request("POST", appsearch_url, headers=app_search_headers, data=payload)

数据Schema处理

以下是从天行数据返回的数据样例:

代码语言:javascript复制
{
    'id': 'ad9e2756f36da74dbf22d846742e6f39', 
    'ctime': '2022-08-04 19:00', 
    'title': '南玻A董事争夺战落幕,前海人寿总经理沈成方当选', 
    'description': '', 
    'source': '澎湃财经', 
    'picUrl': 'https://imagecloud.thepaper.cn/thepaper/image/209/263/264.jpg', 
    'url': 'https://www.thepaper.cn/newsDetail_forward_19312631'
}

我们希望其中的ctime能够被识别为date字段,并且需要符合ISO 8601格式; 而title则是全文检索字段。同时,App search只接受字段名为全小写,因此需要对数据字段做一些处理:

代码语言:python代码运行次数:0复制
            for news in newslist:
                news['picurl']=news['picUrl']
                del news['picUrl']
                news['ctime'] = datetime.strptime(news['ctime'], '%Y-%m-%d %H:%M').isoformat()

然后在界面上选择正确的字段类型:

数据处理-NER

我们还可以通过NLP的NER模型,对数据进行额外的丰富,主要是提取命名实体以提供更多的上下文信息,并支持细颗粒的维度分析,比如,从“南玻A董事争夺战落幕,前海人寿总经理沈成方当选”这个简单的句子中,我们可以由模型提出的两个公司:南玻A前海人寿,提取出一个人名沈成方,以及他的职位总经理

代码语言:javascript复制
{
  "inference_results": [
    {
      "predicted_value": "[南玻A](COMPANY&南玻A)董事争夺战落幕,[前海人寿](COMPANY&前海人寿)[总经理](POSITION&总经理)[沈成方](NAME&沈成方)当选",
      "entities": [
        {
          "entity": "南玻a",
          "class_name": "COMPANY",
          "class_probability": 0.8659130287365686,
          "start_pos": 0,
          "end_pos": 3
        },
        {
          "entity": "前海人寿",
          "class_name": "COMPANY",
          "class_probability": 0.9926979056301972,
          "start_pos": 11,
          "end_pos": 15
        },
        {
          "entity": "总经理",
          "class_name": "POSITION",
          "class_probability": 0.9911902405113301,
          "start_pos": 15,
          "end_pos": 18
        },
        {
          "entity": "沈成方",
          "class_name": "NAME",
          "class_probability": 0.9965376134040271,
          "start_pos": 18,
          "end_pos": 21
        }
      ]
    }
  ]
}

我们可以通过创建如下pipeline的方式,将NER功能嵌入到写入过程中:

在该管道中,我们除了通过NER模型提取出地址,人名,企业,机构等信息外。我们还需要对数据结构进行一定的处理。通过script脚本,将提取的字段从ml.inference.entities对象中上提到_source中,使整体字段结构更扁平化。同时,把结构层次比较深的ml对象,整个删掉。

在实际应用前,可以测试Pipeline的输出:

从上图,我们可以看到,把多余的字段都去掉了,只留下提取的命名实体。

然后可以将这个pipeline应用于索引模板,或者reindex中:

代码语言:javascript复制
POST _reindex
{
  "source": {
    "index": ".ent-search-engine-documents-news"
  },
  "dest": {
    "index": "news_ner",
    "pipeline": "ml_ner"
  }
}

而对于我们的热点分析系统来说,我们需要将这个pipeline应用于新闻信息上,因此,需要跟App search的数据管道进行一个对接。

本次演示中,我们在企业搜索中创建的引擎名为news, 对应的索引为.ent-search-engine-documents-news

(该索引为默认索引,需要勾选包括隐藏索引才能看到):

我们可以直接在Kibana上修改该索引的配置,让其每次写入数据时,都通过我们刚创建的数据处理管理ml_ner提取数据中的命名实体:

向引擎添加命名实体字段

因为命名实体字段是通过ingest pipeline生成的,而非通过App search的API添加。因此,在App search上我们是看不到这些字段的,需要在App search中通过修改schema的方式进行添加:

添加自定义字段的schema添加自定义字段的schema

上图中的gamegovernmentner_address, organization等字段都是命名实体字段,需要通过点击创建架构字段来添加。

到目前为止,我们的数据采集和处理工作基本完成。接下来是需要生成数据查看和分析的系统界面。

数据分析

在这个阶段,我们的工作包括财经信息的查看页面与数据的分析页面

web UI查看数据

按照我们以前的教程,通过搜索UI,我们可以快速创建一个UI来查看我们的数据。通过将NER字段作为过滤条件,我们可以将数据按照不同的维度分片和查询:

配置UI中的筛选项和排序项配置UI中的筛选项和排序项

使用可视化组件分析热点数据

我们可以根据不同的数据维度,了解不同时期新闻的热点:

比如:

过去30天中,最常被媒体提起的人物过去30天中,最常被媒体提起的人物
喜提热搜的公司喜提热搜的公司
热点地区热点地区

甚至可以转化为地图模式:

使用图查询分析热点关系

我们可以使用Graph功能,探索热点数据之间的关系:

总结

在这个案例中,我们只使用了elastic search platform完成了整个财经热点分析系统的建设。回答我们本文开始所说的,当下,像搜索深度理解,智能推荐等需要更为精准的搜索的场景,和NLP的结合已经成为必然。而在Elasticsearch中直接实现NLP,将帮助我们以极简的架构、极低的成本,极快的速度去上线一个包含了NLP功能的搜索项目。

希望本案例能够给各位带来启示,帮助我们更好的在通过Elasticsearch NLP的组合,实现更丰富的用户场景。

0 人点赞