@heavysheep
2021-04-01T11:55:27.000000Z
字数 10629
阅读 620
文档
新增了服务内部根据view的倒排,修改簇心生成优先级
修改了默认CLUSTER.threshold的默认参数为0.7,优先考虑准确率
增加了编辑距离算法,融合单一的群组外数据,强化召回
新增了携带配置参数的请求样例
INCIDENT.AREA
为二级分类,枚举值包括: INCIDENT.HEAT
为[1, 100]的区间。batch_time
字段,字段格式为yyyy-MM-dd HH:mm:ss
;回调时返回batch_time
字段;同时增加INCIDENT.BATCH_TIME
表字段;INCIDENT.HOT_TYPE
和INCIDENT.HOT_STATUS
表字段;SECTION
字段为SECTION_LEVEL_
和SECTION_LEVEL_2
,字段仍以,
作为切分,最多存储6个分类;incident_group
的key部分改为String类型INCIDENT.ID
字段,其他内容不变;IS_DELETE
标注逻辑删除的数据转换为物理删除: 当库中有同名batch_name
的数据,删除其IS_DELETE != 1
或IS_MANUAL != 1
的数据;根据需求,如5所述,当有同批次数据入库,且库中存在因IS_DELETE == 1
或IS_MANUAL == 1
而留库的数据时,如有事件名称相同,进行如下合并处理:
TASK_ID
以标注,不更改原留库数据的其他字段,不插入同名新聚类数据;RAW_DATA
指向旧的留库数据ID。根据新测试数据,增加了大量normalize逻辑,尽可能过滤无用unicode、乱码、前端标签、原数据分类等字符,防止影响聚类效果和聚类结果名称;尽可能过滤无用unicode等会影响关键词提取的内容;降低了由于乱码导致入库事务失败的可能性;
.env文件是项目部署的基础配置,示例说明如下
# [BASE]
ENV=TEST # [环境标签] 枚举为DEV/TEST/PROD区别是日志颗粒度不同
TZ=Asia/Shanghai # [时区配置] 正常不需修改
# [ORACLE]
ORACLE_HOST=172.16.5.129 # [ORACLE HOST] 按需修改
ORACLE_USER=dgrd # [ORACLE USER] 按需修改
ORACLE_PASSWORD=dgrd # [ORACLE PASS] 按需修改
ORACLE_PORT=1521 # [ORACLE PORT] 按需修改
ORACLE_CHARSET=UTF8MB4 # [ORACLE_CHARSET] 按需修改
DB=orcl # [ORACLE_DB] 按需修改
# [REDIS]
REDIS_HOST=redis # [REDIS HOST] 指向同网络名为redis的容器,正常不需修改
REDIS_PORT=6379 # [REDIS PORT] 正常不需修改
REDIS_PASSWORD=xxadksll1 # [REDIS_PASSWORD] 正常不需修改
DATA_CACHE=0 # [缓存DB索引] 正常不需修改
CELERY_BROKER=1 # [队列DB索引] 正常不需修改
# [Middle Platform]
MP_SERVER=incident_cluster_server:10001 # [中台位置] 队列任务基于此地址找到中台,正常不需修改
# [SERVICE]
CLUSTER_SERVICE=cluster_service:8000 # [聚类服务位置] 正常不需修改
TAGGING_SERVICE=tagging_service:9190 # [抽取服务位置] 正常不需修改
# [CALLBACK API]
# [回调API] 按需修改,填写可访问的API
# 当任务成功时,会向此API发送JSON数据形式POST请求告知聚类枚举信息
CALLBACK_API=incident_cluster_server:10001/comm/callback
_default.json位于部署项目的task.d文件下,修改后下次任务生效。
注意点: 常规情况下,media_type
参数为int值,但由于此处作为json的key使用,因此改为string格式。系统中有且只有此处的media_type
为string。
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
CLUSTER | Object | 聚类配置 | |
--size | Int | 500 | 聚类结果数量 超过热度的数据,以热度排序并做截断 |
--strategy | String | 1v1 | 聚类策略,枚举为: 1v1 : 1对1比对 |
--threshold | Float | 0.7 | 聚类阈值:超过阈值视为相似,值越大匹配越严格 |
KEYWORD | Object | 关键词配置 | |
--keep_num | Int | 10 | 保留关键词数量; 值必须>=0; 抽取的关键词在表中以 , 区隔 |
--threshold | Float | 0.4 | 保留关键词阈值:权重低于此阈值的关键词不会被保存 值必须>=0 抽取的关键词在表中以 , 区隔 |
HEAT | Object | 热度配置 | |
--strategy | String | "weight" | 数据评论量;值必须>=0 |
--value_weight | Object | 运算参数权重配置 | |
----view | Float | 1 | 浏览量权重: 热度将加和此参数*权重的值 |
----comment | Float | 10 | 评论量权重: 热度将加和此参数*权重的值 |
----forward | Float | 20 | 转发量权重: 热度将加和此参数*权重的值 |
--classification_weight | Object | {} | 类型权重配置 |
----(item) | Float | {} | 热度基于以上计算后,将乘以匹配此分类权重的值 示例: {"宏观经济": 2,"北京": 5} 即数据包含宏观经济时热度*2 包含北京时*5 同时包含即为*10 |
--media_weight | Object | 0 | 数据转发量;值必须>=0 |
----(item) | Float | {} | 热度基于以上计算后,将乘以匹配此分类权重的值 示例: {"1": 1.5, "5":2} 即新闻类型热度*1.5 微博类型热度*5 |
流程
注意点
[POST](json) {cluster_service}/api/cluster
参数名称 | 参数值 |
---|---|
Content-Type | application/json |
参数 | 类型 | 是否必填 | 默认值 | 说明 |
---|---|---|---|---|
batch_name | String | 是 | 批次名称 | |
batch_time | String | 是 | 批次时间;yyyy-MM-dd HH:mm:ss格式 | |
raw_data | List(Object) | 是 | 数据内容 | |
--id | String | 是 | 数据ID | |
--title | String | 是 | 数据标题;标题为空时填写空字符串"" | |
--content | String | 是 | 数据正文;正文为空时填写空字符串"" | |
--datetime | String | 是 | %Y-%m-%d %H:%M:%S格式datetime字符串 | |
--classifications | List(String) | 是 | 应包含板块、地域、情感三项分类,枚举参见分类枚举 | |
--media_type | Int | 是 | 媒体类型; 枚举参见媒体类型枚举 |
|
--view | Int | 否 | 0 | 数据浏览量; 值必须>=0; |
--comment | String | 否 | 0 | 数据评论量; 值必须>=0 |
--forward | String | 否 | 0 | 数据转发量; 值必须>=0 |
task_info | Object | 否 | 单次任务规则 未传此参数时,读取预置默认规则 具体参数信息可见default.json |
此业务中,classifications应包含板块分类
,地域分类
,情感分类
等三项分类业务中的二级分类枚举,枚举外的分类不会被处理,枚举如下
板块分类枚举
地域分类枚举
情感分类枚举
示例为:
{
...
"classifications": ["宏观经济", "中性", "国内"],
...
},
{
...
"classifications": ["娱乐", "正面"],
...
}
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
code | Int | 是 | 状态码 |
data | Object | 是 | 分类结果列表 |
--check | Bool | 是 | 检查是否通过 成功: True http状态码202 失败: False http状态码400 |
message | String | 是 | 处理信息 |
# 请求数据
{
"batch_name": "2",
"batch_time": "2020-11-17 15:00:00",
"raw_data": [{
"id": "6034",
"title": "中央发话后,公安系统揪出了7个“老政法”",
"content": "原标题:昆山世硕“扔证件”后续:员工排队4小时离职,母公司营收创新高 近日,一段“扔证件”的视频在网上流传。视频显示,江苏昆山的世硕电子(昆山)有限公司工作人员给新员工发证件时,随手将员工证件丢在地上,新员工只能弯腰去捡。事发后,该公司曾发声明,表示主管将率团队向员工致歉。 9月7日下午,近日已从昆山世硕离职的员工告诉新京报贝壳财经记者,近日公司出现离职现象,离职手续并不复杂,但仍然排队超过4小时。不过也有员工表示,工厂(世硕)本身人员流动性就比较高,有人辞职也属于正常现象。记者在招聘网站上看到,昆山世硕如今仍在大量招人,如品保工程师岗位招聘70人,生产组长招聘100人等。 新京报贝壳财经记者9月7日拟就员工离职及公司生产情况采访昆山世硕,多次致电均未能接通。 据昆山世硕官网介绍,公司的母公司为和硕联合科技股份有限公司。后者2019年年报介绍,公司营业收入创新高,原因包括人力规划及原物料管理精实,制造成本达到有效管控。 年报还显示,公司酬金级别中,等级最高的为每年新台币500万元-1000万元,有10名高管在此列,其中就包括昆山世硕的法定代表人张天宝。 员工为离职排队4小时 “今天我去昆山世硕办离职,人太多了,排了4个小时队”。9月7日下午,一名刚从世硕离职的员工向新京报贝壳财经记者表示,他上午办理了离职手续,手续并不复杂,需要在表格内填写姓名、身份证号、银行卡号、工号等信息,但由于办理人数太多,排队等了很久,“一直排到了大马路上”。 还有多名世硕员工告诉记者,最近世硕离职人数众多。有员工说,他们宿舍原本有9个人,目前已经全部离职。还有员工称,自己所在车间原本有上千工人,据他所知已经离职过半。还有一位名为“子弹壳”的网友,自称昆山世硕已离职员工,其在腾讯新闻话题栏目表示,有车间出现大量人员离职。 该员工还称,如果是通过中介介绍入职的,在一定条件下,员工可以拿到一笔“返费”,今天离职的人应该大部分是拿到了返费的人。 记者在昆山世硕直招的公众号上看到,公司本月招聘帖中,给出的员工底薪为2280元、全勤奖金100元、绩效奖金最高400元、岗位津贴最高300元,此外还有夜班补贴和加班工资。其中,打卡55天的返费为1.35万元-1.45万元。 不过,也有员工表示,在没有发生此事前,世硕也有人员流动,各家工厂的人员流动性原本就比较大,",
"comment": 72897,
"datetime": "2020-09-07 23:52:24",
"classifications": ["宏观经济", "商业", "金融", "国内", "正面"],
"media_type": 1,
"view": 7040,
"forward": 3284
},
...]
}
# 请求携带任务配置,可在此基础进行修改
{
"batch_name": ...,
"batch_time": ...,
"raw_data": ...,
"task_info": {
"CLUSTER": {
"size": 500,
"strategy": "1v1",
"threshold": 0.7
},
"KEYWORD": {
"keep_num": 10,
"threshold": 0.4
},
"HEAT": {
"strategy": "weight",
"value_weight": {
"view": 1,
"comment": 10,
"forward": 20
},
"classification_weight": {
"北京": 1,
"宏观经济": 1
},
"media_weight": {
"1": 1,
"2": 1,
"3": 1,
"4": 1,
"5": 1
}
}
}
}
# 响应数据: 成功
{
"code": 0,
"data":{
"check": True
}
"message": "请求成功"
}
# 响应数据: 失败
{
"code": 1101,
"data":{
"check": False
}
"message": "index:[1] param [content] must be string"
# 第二条数据的正文字段不为string
}
此接口不会修改IS_MANUAL参数 != 0的数据
[PUT](json) {cluster_service}/api/cluster
参数名称 | 参数值 |
---|---|
Content-Type | application/json |
参数 | 类型 | 是否必填 | 默认值 | 说明 |
---|---|---|---|---|
id | Int | 是 | 数据ID | |
data_id | List(String) | 否 | 数据ID | |
title | str | 否 | 热点标题 | |
keyword | List(String) | 否 | 热点关键词 | |
heat | Int | 否 | 热点热度值 | |
section | List(String) | 否 | 热点板块分类 当新标签不在定义中则报错,详见分类枚举 |
|
area | List(String) | 否 | 地域分类 当新标签不在定义中则报错,详见分类枚举 |
|
sentiment | String | 否 | 0 | 热点情感分类 当新标签不在定义中则报错,详见分类枚举 |
total_view | Int | 否 | 0 | 热点总浏览(点击)量 |
total_comment | Int | 否 | 0 | 热点总评论量 |
total_forward | Int | 否 | 热点总转发量 | |
total_count | Int | 否 | 热点数据总量 | |
sort_index | Float | 否 | 热点排序位置 |
此业务中,classifications应包含板块分类
,地域分类
,情感分类
等三项分类业务中的一级分类枚举,枚举外的分类不会被处理,枚举如下
板块分类枚举
地域分类枚举
情感分类枚举
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
code | Int | 是 | 状态码 |
data | Object | 是 | 分类结果列表 |
--row_count | Int | 是 | 实际修改数据量 |
message | String | 是 | 处理信息 |
# 请求
{
"id": 230,
"title": "修改后的名字",
"heat": 2300,
"section": ["政治", "科技"]
}
# 响应
{
"code": 0,
"data": {
"row_count": 1
},
"message": "更新成功"
}
此接口不会删除IS_MANUAL参数 != 0的数据
[DELETE](json) {cluster_service}/api/cluster
参数名称 | 参数值 |
---|---|
Content-Type | application/json |
参数 | 类型 | 是否必填 | 默认值 | 说明 |
---|---|---|---|---|
id | List(Int) | 是 | 数据ID列表 |
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
code | Int | 是 | 状态码 |
data | Object | 是 | 分类结果列表 |
--row_count | Int | 是 | 实际修改数据量 |
message | String | 是 | 处理信息 |
# 请求
{
"id": [230, 231]
}
# 响应
{
"code": 0,
"data": {
"row_count": 2
},
"message": "删除成功"
}
热度计算方式可支持多种策略配置,当前的weight计算方式为
(1 + view*view权重 + comment*comment权重 + forward*forward权重) * (多个)匹配分类权重 * 类型权重
# 假设此时有以下数据被聚为同一时间,参数为
{
"view": 100,
"comment": 50
"forward": 20,
"media_type": 5,
"classifications": ["宏观经济"]
},
{
"view": 200,
"comment": 10
"forward": 5,
"media_type": 4,
"classifications": ["商业", "北京"]
}
# 同时有规则
"HEAT": {
"strategy": "weight",
"value_weight": {"view": 1, "comment": 10, "forward": 20},
"classification_weight": {"宏观经济": 2, "商业": 1.5, "北京": 5},
"media_weight": {"5": 2, "4": 1.5}
}
则聚类结果按"weight"策略计算时,计算方式为
数据1: (1 + 100 * 1 + 50 * 10 + 20 * 20) * 2 * 2 = 4004
数据2: (1 + 200 * 1 + 10 * 10 + 5 * 20) * 1.5 * 5 * 1.5 = 4511.25
则最终该事件热度为: 4004 + 4511.25 = 8515.25 ~= 8515
[POST](json) http://{CALLBACK_API}
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
status | String | 是 | 任务状态 succes: 任务成功 failed: 任务失败 |
batch_name | String | 是 | 传入的batch_name字段 |
batch_time | String | 是 | 传入的batch_time字段 |
incident_group | Object | 是 | 聚类关系 |
--{key} | String | 否 | 字符串形式INCIDENT.ID 字段 |
--{value} | List(String) | 否 | 聚类数据ID |
message | String | 否 | 错误信息 |
# 任务成功
{
"status": "sucess",
"batch_name": "2",
"batch_time": "2020-11-17 15:00:00",
"incident_group": {
"7185": ["6034", "7528"],
"7186": ["2438", "2410"],
"7187": ["9321"],
"7188": ["6216", "6125"],
"7189": ["6596", "6032"],
"7190": ["9284"],
"7191": ["3220"],
"7192": ["7944"],
"7193": ["8153"],
"7194": ["4658"],
"7195": ["5605"],
"7196": ["7589"],
"7197": ["2318"],
"7198": ["3811", "3189"],
"7199": ["7670"],
"7200": ["9908"],
"7201": ["5388"],
"7202": ["9299"],
"7203": ["9141"],
"7204": ["5466"],
"7205": ["8530"],
"7206": ["8399"],
"7207": ["3200"],
"7208": ["2045"],
"7209": ["4569"],
"7210": ["2575", "2256"],
"7211": ["2912"],
"7212": ["2682"],
"7213": ["9850"],
"7214": ["4016"],
"7215": ["7266"],
"7216": ["2540"],
"7217": ["5198"],
"7218": ["5447"],
"7219": ["7448"],
"7220": ["9385"],
"7221": ["4245"],
"7222": ["7564"],
"7223": ["5374"],
"7224": ["5589"],
"7225": ["6474"],
"7226": ["3716"],
"7227": ["6995"],
"7228": ["6549"]
}
}
# 任务失败
{
"status": "failed",
"batch_name": "2",
"batch_time": "2020-11-17 15:00:00",
"incident_group": {},
"message": "request tagging service time out error"
}
注意: 以下所有列均有非空约束, 由于ORACLE非空约束不允许空字符串""
,所有的字符空值为" "
参数 | 类型 | 索引 | 说明 |
---|---|---|---|
ID | NUMBER(*,0) | 主键 | 热点数据ID |
TASK_ID | VARCHAR2(64 CHAR) | 单列索引 | 任务UUID,用以识别一次任务产生的热点 |
BATCH_NAME | VARCHAR2(64 CHAR) | 单列索引 | 输入的batch_name参数;另以此参数处理去重逻辑 |
BATCH_TIME | DATE | 单列索引 | 输入的batch_time参数 |
DATA_ID | CLOB | 热点包含数据ID,多个ID以, 区隔 |
|
MEDIA_TYPE | NUMBER(*,0) | 媒体类型 | |
CLUSTER_TIME | DATE | 聚类时间,%Y-%m-%d %H:%M:%S格式 | |
TITLE | VARCHAR2(256 CHAR) | 热点标题 | |
KEYWORD | CLOB | 热点关键词,多关键词以, 区隔 |
|
HEAT | NUMBER(*,0) | 热点热度值;缩放为[1, 100] | |
SENTIMENT | VARCHAR2(64 CHAR) | 热点情感 | |
SECTION_LEVEL_1 | VARCHAR2(64 CHAR) | 热点板块一级分类,多个分类以, 区隔,最多不超过6个 |
|
SECTION_LEVEL_2 | VARCHAR2(64 CHAR) | 热点板块二级分类,多个分类以, 区隔,最多不超过6个 |
|
AREA | VARCHAR2(64 CHAR) | 热点地域(二级分类) | |
TOTAL_VIEW | NUMBER(*,0) | 热点总浏览(点击)量 | |
TOTAL_COMMENT | NUMBER(*,0) | 热点总评论量 | |
TOTAL_FORWARD | NUMBER(*,0) | 热点总转发量 | |
TOTAL_COUNT | NUMBER(*,0) | 热点数据量 | |
SORT_INDEX | FLOAT(126) | 热点排序位置 | |
HOT_TYPE | CHAR(1) | 热点事件类型 0:热点 1:榜单 默认值: 0 |
|
HOT_STATUS | CHAR(1) | 关联关系更新solr状态 0:未完成 1:已完成 默认值: 0 |
|
CREATED_TIME | DATE | 数据创建时间 | |
LAST_UPDATE_TIME | DATE | 上次更新时间 | |
IS_DELETE | NUMBER(*,0) | 是否已删除 0: 未删除 1:已删除 |
|
IS_MANUAL | NUMBER(*,0) | 手动标记 0: 自动 1:手动 |
参数 | 类型 | 索引 | 说明 |
---|---|---|---|
ID | (*,0) | 主键 | 数据ID |
INCIDENT_ID | (*,0) | 单列索引 | 热点数据ID |
DATA_ID | (*,0) | 原始数据ID | |
HEAT | (*,0) | 数据热度(根据脚本实时获得数据计算) | |
view | NUMBER(*,0) | 数据浏览(点击)量(实时获得值) | |
comment | NUMBER(*,0) | 数据评论量(实时获得值) | |
FORWARD | NUMBER(*,0) | 数据转发量(实时获得值) | |
CREATED_TIME | DATE | 数据创建时间 | |
LAST_UPDATE_TIME | DATE | 上次更新时间 | |
IS_DELETE | NUMBER(*,0) | 是否已删除 0: 未删除 1:已删除 |
以下根据Solr数据生成
值 | 类型 |
---|---|
1 | 新闻 |
2 | 论坛 |
3 | 博客 |
4 | 视频 |
5 | 微博 |
6 | 邮箱 |
7 | 图片 |
8 | 网盘 |
9 | 热搜词 |
10 | 热榜 |
11 | 两首 |
12 | 直播 |
13 | 自媒体 |
14 | 中小网站新闻 |
15 | 中小网站自媒体 |
16 | 中小网站新闻论坛 |
17 | 中小网站新闻博客 |
18 | 问答 |
19 | 微头条 |
20 | 两会论坛 |
51 | 热门微博 |
99 | 其他 |
部分逻辑应用于算法聚类前的预处理,可能有帮助
# 删除常规前端标签
text = remove_tags(text)
# 删除左右两边空格、换行符
text = text.strip()
# 标准化非utf-8编码,防止处理时一些编码问题
text = unicodedata.normalize("NFC", text)
...
# 低质量数据br换行其他unicode乱码耦合情况较多,先清除br/
re.compile(r"br/")
# 清除类似 !--{IMAGE_1}-- 等标签
re.compile(r"!--[{}A-Z0-9_]*--")
# 暴力清除可能的unicode字符
# 会影响到以字符、数字开头的文章
# 没有精准控制长度为4的原因是多前端标签耦合严重,如果不一次清除干净,段落间留下的p很难处理
re.compile(r"\\u[0-9a-z=\\/-]*")
# 清除常见来自新浪新闻的特别声明
# 该声明后如有内容,基本都是其他文章的链接
re.compile(r"特别声明:.*")