AI Challenger 2018:细粒度用户评论情感分类冠军 - 程惠阁

关注了一些 ai 相关的。原文查看机器之心:https://www.jiqizhixin.com/articles/2019-01-28-16

2018年8月-12月,由美团点评、创新工场、搜狗、美图联合主办的“AI Challenger 2018全球AI挑战赛”历经三个多月的激烈角逐,冠军团队从来自全球81个国家、1000多所大学和公司的过万支参赛团队中脱颖而出。其中“后厂村静静”团队-由毕业于北京大学的程惠阁(现已入职美团点评)单人组队,勇夺“细粒度用户评论情感分类”赛道的冠军。本文系程惠阁对于本次参赛的思路总结和经验分享,希望对大家能够有所帮助和启发。

img

背景

在2018全球AI挑战赛中,美团点评主要负责了其中两个颇具挑战的主赛道赛题:细粒度用户评论情感分析和无人驾驶视觉感知。其中NLP中心负责的细粒度用户评论情感分析赛道,最受欢迎,参赛队伍报名数量最多,约占整个报名团队的五分之一。

细粒度用户评论情感分析赛道提供了6大类、20个细分类的中文情感评论数据,标注规模难度之大,在NLP语料特别是文本分类相关语料中都属于相当罕见,这份数据有着极其重要的科研学术以及工业应用价值(目前在大众点评App已经可以看到20个类别的情感标签了)。

1. 工具介绍

在本次比赛中,采用了自己开发的一个训练框架,来统一处理TensorFlow和PyTorch的模型。在模型代码应用方面,主要基于香港科技大学开源的RNetMnemonicReader做了相应修改。在比赛后期,还加入了一个基于BERT的模型,从而提升了一些集成的效果。

2. 整体思路

整体将该问题看作20个Aspect的情感多分类问题,采用了传统的文本分类方法,基于LSTM建模文本,End2End多Aspect统一训练。

文本分类是业界一个较为成熟的问题,在2018年2月份,我参加了Kaggle的“作弊文本分类”比赛,当时的冠军团队主要依靠基于翻译的数据增强方法获得了成功。2018年反作弊工作中的一些实践经验,让我意识到,数据是提升文本分类效果的第一关键。因此,我第一时间在网络上寻找到了较大规模的大众点评评论语料,在Kaggle比赛的时候,NLP的语言模型预训练还没有出现,而随着ELMo之类模型的成功,也很期待尝试一下预训练语言模型在这个数据集合上的整体效果。

3. 基础模型思路

首先,尝试了不使用预训练语言模型的基础模型,基于Kaggle Toxic比赛的经验,直接使用了当时表现最好的LSTM Encode + Pooling作为基线模型。在Kaggle的比赛中,大家实验的普遍结果是针对中长文本的分类任务的最佳单模型,都是基于RNN(LSTM/GRU)或者部分基于RNN的模型,比如RCNN、Capsule + RNN这样的模型,而其他的模型,比如单纯的CNN结构相对表现较差,主要可能是因为RNN模型能更好地捕获相对较长距离的顺序信息。

img

4. 模型层面优化

在基线模型的基础上,效仿阅读理解常见的做法,增加了Self Attention层(计算文本到文本自身的Attention权重),并将Attention之后的输出和原始LSTM输出,采用Gate(RNet)或者Semantic Fusion(MnemonicReader)的方式进行融合。

img

5. 模型细节处理

更宽的参数更多的模型效果更好

  • LSTM效果好于GRU。
  • Hidden size 400 > 200 > 100。
  • Topk Pooling + Attention Pooling的效果好于单独的Max或者Attention Pooling。
  • 共享层前置,Pooling层 和最后Fc层不同aspect参数独占效果更好(来自赛后实验,以及其他团队经验)。

这里推测主要原因:是这个数据集有20个Aspect,每个Aspect分4个不同的类别,所需要的参数相对较多。

三角学习率调节效果最佳

  • 参考BERT开源代码的学习率设置带来较大效果提升。

采用Word + Char的词建模方式

  • 这种建模方式能结合分词和字符粒度切分的好处,最大限度避免词汇UNK带来的损失。
  • 注意对比Kaggle Toxic比赛那次比赛是英文语料,对应英文,当时的实验结果是Word + Ngram的建模效果更好,收敛更快,所以针对不同NLP任务,我们需要具体进行分析。

采用尽可能大的词表

和其他团队相比,我采用了更大的词表14.4W(Jieba分词),19.8W(Sentence Piece Unigram分词),依靠外部大众点评评论数据基于fastText预训练词向量,能够支持更大的词表。同时为了避免训练过拟合,采用了只Finetune训练中高频的词对低频词固定词向量的处理方式。

最开始,预计情感相关的词汇相对较少,不需要较大的词表,但是实验过程中发现更大的词表相对地能够提升性能,前提是利用较多的外部数据去比较好的刻画训练数据中低频词的向量。在理论上,我们可以采用一个尽可能大的词表在预测过程中去尽可能的减少UNK的存在(有论文的结论是对应UNK不同的词赋于不同随机向量效果,好于一个固定的UNK向量。这里类似,如果我们赋予一个基于无监督外部数据,通过语言模型训练得到的向量则效果更好)。

6. 预训练语言模型

这部分是模型效果提升的关键,这里采用了ELMo Loss。在简单尝试了官方的ELMo版本之后,感觉速度相对比较慢,为此,采用了自己实现的一个简化版的ELMo,实质上只使用了ELMo的Loss部分。

在当前双层LSTM Encoder的基础上,采用了最小代价的ELMo引入,也就是对当前模型的第一层LSTM进行基于ELMo Loss的预训练,而Finetune的时候,模型结构和之前完全不变,只是第一层LSTM以及词向量部分采用的ELMo预训练的初始化结果,另外在ELMo的训练过程中,也采用了基于fastText的词向量参数初始化。这个设计使得ELMo训练以及Finetune训练的收敛,都加快了很多,只需要大概1小时的ELMo训练,就能在下游任务产生明显受益。值得一提的是,ELMo和Self Attention的搭配在这个数据集合效果非常好。

img

img

7. 模型集成

为了取得更好的模型多样性,采用了多种粒度的分词方式,在Jieba分词的主要模型基础上,同时引入了基于SentencePiece的多种粒度分词。SentencePiece分词能带来更短的句子长度,但是分词错误相对Jieba略多,容易过拟合,因此采用了只Finetune Char向量,固定词向量的策略来避免过拟合。多种粒度的分词配合Word + Char的建模方式带来了很好的模型多样性。

此外,模型维度的多样性来源自RNet结构和MnemonicReader结构,以及BERT模型的结构的不同。

在模型选择的时候选取了平均F1值最优的轮次模型,集成的时候采用了按Aspect效果分开加权集成的方式(权重来自Valid数据的F1分值排序)。基于以上的多样性策略,只需要7个单模型集成就能取得较好的效果。

8. 关于BERT

在实验中基于Char的BERT单模型,在本次比赛中并没有取得比ELMo更好的效果,受限于512的长度和只基于Char的限制,目前看起来BERT模型在这个数据集合更容易过拟合,Train Loss下降较快,对应Valid Loss效果变差。相信通过适当的优化BERT模型能取得更好的效果。

9. 后续优化

  • F1的优化是一个有意思的方向。本次比赛中,没有对此做特殊处理,考虑到F1是一个全局优化值,如果基于Batch强化学习,每个Batch可能很难拟合稀有样本分布。
  • BERT的进一步优化。因为BERT出现之前,基于Transformer的模型在长文本分类效果大都是差于基于LSTM的模型的,所以如果我们按照BERT的Loss去预训练基于LSTM而不是Transformer的模型,在分类问题层面的效果如何?另外,在这个数据集合基于Transformer的BERT,能否取得比ELMo更好的分类效果?

对话AI Challenger 2018冠军:程惠阁

Q:谈谈对本次参赛的感受?

程惠阁:作为一个多年的算法从业者,我真实的感受到在AI时代,技术更新非常之快,比如席卷而来的ELMo、BERT等预训练语言模型在工业界影响力之大。包括美团在内的很多公司都快速跟进并上线,而且取得了很好收益,因此技术人员时刻保持学习的心态是非常重要的。

而比赛和工作存在很大的不同,比赛相对更加单纯明确,比赛可以使我在最短时间去学习实验验证一些新的技术,而在标准数据集合验证有效的模型策略,往往在工作中也有实际的价值。对于比赛以及工作中的模型开发,我觉得比较重要的一点首先要做好细致的模型验证部分,在此基础上逐步开发迭代模型才有意义。比如在这次比赛中,我从一开始就监控了包括整体以及各个Aspect的包括F1、AUC、Loss等等各项指标。

Q:对学习算法的新同学有哪些建议?

程惠阁:如果有时间,可以系统地学习一些名校的深度学习相关的课程,还有很重要的一点,就是实践,我们可以参加去学校项目或者去大公司实习,当然也可以利用AI Challenger、Kaggle这样的竞赛平台进行实践。

Q:为什么会选择参加细粒度用户评论情感分类这个赛道?

程惠阁:因为我之前参加过类似的比赛,并且做过文本分类相关的工作,对这个赛道的赛题也比较感兴趣。

Q:本次比赛最有成就感的事情是什么?

程惠阁:不断迭代提升效果带来的成就感吧,特别是简化版ELMo带来的效果提升。

Q:参赛过程中,有哪些收获和成长?

程惠阁:作为一个TensorFlow重度用户,我学会了使用PyTorch并且体验到PyTorch带来的优雅与高效。体验到了预训练语言模型的威力。在比赛中和比赛后,我也收获了很多志同道合的朋友,和他们的交流学习,也帮助我提高了很多。

更重要的是,因为这次比赛,我加入了美团点评这个大家庭,入职这段时间,让我真切地感受到美团点评为了提升用户体验,为了让用户吃的更好,生活更好,在技术方面做了大量的投入。


又一个 webdav 服务器

背景

前几天记录了一篇关于 webdav 的文章,但那个 webdav 我始终无法解决写数据的问题。于是又找到了这个 webdav,虽然也有瑕疵, 不过总算满足了目前写文件的需求。

搭建 WebDAV

编辑 docker-compose.yml 文件:

version: '3.2'

  webdav-zotero:
    image: morrisjobke/webdav
    container_name: webdav-zotero
    environment:
      USERNAME: kelu
      PASSWORD: kelu
    volumes:
      - zotero:/var/webdav/zotero
    restart: always
    ports:
      - "8080:80"

用户名密码均为 kelu, 运行即可。

docker-compose up -d

使用 http://ip:8080/webdav 即可访问到服务。


一个电商需求分析demo - LaravelChina

一篇清晰简约的需求分析的文章。原文:https://laravel-china.org/courses/laravel-shop/5.7/module-division/2738

用例分析

Laravel Shop 是我们整套课程将要构建的项目名称,是一个单商户的电商系统。本章节将简单地从产品用例的角度上来分析 Laravel Shop 的需求,好让大家对我们即将开发的项目有个基础的概念。我们主要从以下三种元素入手:

  1. 角色
  2. 信息
  3. 动作

接下来做单独分解。

1. 角色

在我们的 Laravel Shop 里,将会出现以下角色:

  • 游客 —— 没有登录的用户;
  • 用户 —— 注册用户, 可以购买商品;
  • 运营 —— 可以上架、下架商品,处理订单;
  • 管理员 —— 权限最高的用户角色,可以管理运营。

2. 信息结构

主要信息有:

  • 用户 —— 模型名称 User;
  • 收货地址 —— 模型名称 UserAddress,包含地址和收货人姓名、电话;
  • 商品 —— 模型名称 Product,比如 iPhone X 就是一个商品;
  • 商品 SKU —— 模型名称 ProductSKU,同一商品下有个别属性可能有不同的值,比如 iPhone X 256G 和 iPhone X 64G 就是同一个商品的不同 SKU,每个 SKU 都有各自独立的库存;
  • 订单 —— 模型名称 Order;
  • 订单项 —— 模型名称 OrderItem,一个订单会包含一个或多个订单项,每个订单项都会与一个商品 SKU 关联;
  • 优惠券 —— 模型名称 CouponCode,订单可以使用优惠券来扣减最终需要支付的金额;
  • 运营人员 —— 模型名称 Operator,管理员也属于运营人员。

3. 动作

角色和信息之间的互动称之为『动作』,动作主要有以下几个:

  • 创建 Create
  • 查看 Read
  • 编辑 Update
  • 删除 Delete

用例

1.游客

  • 游客可以查看商品列表;
  • 游客可以查看单个商品内容。

2. 用户

  • 用户可以查看自己的收货地址列表;
  • 用户可以新增收货地址;
  • 用户可以修改自己的收货地址;
  • 用户可以删除自己的收货地址;
  • 用户可以收藏商品;
  • 用户可以将商品加入购物车;
  • 用户可以将购物车中的商品打包下单;
  • 用户可以在下单时使用优惠券;
  • 用户可以通过微信、支付宝支付订单;
  • 用户可以查看自己的订单信息;
  • 用户可以对已支付的订单申请退款;
  • 用户可以将已发货的订单标记为确认收货;
  • 用户可以对已购买的商品发布评价。

3. 运营

  • 运营可以看到所有的用户列表;
  • 运营可以发布商品;
  • 运营可以编辑商品内容;
  • 运营可以编辑商品 SKU 及其库存;
  • 运营可以下架商品;
  • 运营可以将用户已支付的订单标记为已发货;
  • 运营可以对申请退款的订单执行退款;
  • 运营可以创建、编辑、删除优惠券。

4. 管理员

  • 管理员可以查看运营人员列表;
  • 管理员可以新增运营人员;
  • 管理员可以编辑运营人员;
  • 管理员可以删除运营人员。

file

很多时候,当开发团队开始启动一个项目时,区分功能模块的优先顺序尤其重要,否则你都不知道从哪里下手。这里我们使用一个简单的分析框架,来决策功能模块的开发优先级。你也可以使用其对大部分的 Web 项目进行模块开发的优先级分析。

模块清单

首先,基于我们的需求分析,我们将系统拆分成如下几大模块:

  • 用户模块
  • 商品模块
  • 订单模块
  • 支付模块
  • 优惠券模块
  • 管理模块

依赖关系

有了模块清单,接下来我们需要思考,他们之间的依赖关系是怎样的。在上面的功能清单中,『订单模块』依赖于『用户模块』和『商品模块』,『支付模块』和『优惠券模块』又依赖于『订单模块』。各个模块之间的依赖关系可以用下图来表示:

file

上层的模块依赖于下层的模块,因此在开发过程中我们会优先构建下层的模块。

开发顺序

所以我们各个模块开发的顺序如下:

  1. 用户模块
  2. 商品模块
  3. 订单模块
  4. 支付模块
  5. 优惠券模块

『管理模块』是一个特殊的模块,既包含本身的逻辑(管理后台的权限控制等),又与其他业务模块都有关联,因此在开发过程中会与其他模块穿插开发。

MVP 产品

是的,作为工程师,我们不需要了解产品的方方面面,那是产品经理的工作。但是作为一位优秀的开发者,在开发项目时,对将要完成的产品 MVP 要了然于胸,MVP 是 Minimum Viable Prodcut (最小化可行性产品)的简称。如何得出产品的 MVP 产品呢?可以先问这样的问题:

对于这个产品来讲,哪些功能是必不可缺的?

电商产品是一个用户购买商品的地方,产品存在的核心价值是『用户购买商品』,那首先需要用户、然后需要商品、购买需要付款。所以在我们的电商项目里,用户、商品、订单和支付模块都是必不可少的。

优惠券功能并不是购物流程中必备的一环,属于附加的功能,锦上添花的东西。我们在设计和开发项目时,应优先完成基础的功能,让流程能尽快跑起来,尽早交付,快速迭代。

Web 开发是个速度至上的领域,最小产品功能先上,测试的工作量也不会太大。不能憋大招,一个上线就是一大堆功能,复杂度增加的是无限的开发和调错时间,项目上线期限无尽延长。另一方面,用户能在最短时间内接触到产品,产品经理也可以尽快听到用户的反馈,及时调整产品战略,产品离成功会更进一步,这是一个多赢的方案。

这个思路也与敏捷开发的思路不谋而合:

敏捷开发即是以用户的需求进化为核心,采用迭代、循序渐进的方法进行软件开发。

结语

功能模块的开发优先级,我们已经有了,接下来就是要动手开始写代码了。


批量调整word中插入的图片大小 - 到处玩的

使用宏,alt+F8进入宏查看界面,点击创建,输入以下代码,设置图片高、宽,单位:厘米。f5或保存后退出宏编辑界面。

然后alt+F8进入宏查看界面,点击运行即可。

Sub 批量设置图片大小()
'
' Macro 宏
'
'
Myheigth = 9
Mywidth = 12
On Error Resume Next '忽略错误
For Each iShape In ActiveDocument.InlineShapes
iShape.Height = 28.345 * Myheigth '设置图片高度为任意cm
iShape.Width = 28.345 * Mywidth '设置图片宽度
Next
For Each Shape In ActiveDocument.Shapes
Shape.Height = 28.345 * Myheigth '设置图片高度为任意cm
Shape.Width = 28.345 * Mywidth '设置图片宽度
Next
End Sub

参考资料


搭建个人 webdav 服务器

背景

对于技术飞速发展的今天来说, webdav 算是一个相对古老的协议,可能一些人也只是瞥过这个单词,并不知道是用来做什么的。由于我iPad上的一个软件 “Document” 的服务中有这样一个协议(还有Dropbox/google drive/SMB/ftp等),想起以前曾经用过 OmniFocuse 这类的应用,也支持自定义的 WebDAV协议,临时起意也起一个自己的 WebDAV 服务。接下来介绍如何使用容器搭建。

什么是 WebDAV

首先要了解HTTP协议。 HTTP协议定义了几种请求: GET, POST,PUT等用来下载文件上传数据。

WebDAV 则在标准的HTTP协议上扩展了特有的请求方式: PROPFIND, MOVE, COPY等。 然后用这些请求,操作web服务器上的磁盘,我们可以将其当作网盘来使用。

WebDAV 标准得到了广泛的成功,所有的现代操作系统拥有内置的对普通 WebDAV 的支持,许多流行的应用程序也可以使用 WebDAV,如 Microsoft Office,Dreamweaver 和 Photoshop。

搭建 WebDAV

这么成熟的协议docker社区肯定已经有很多相关的镜像了。我使用的是这个镜像: jgeusebroek/docker-webdav 。这个镜像基于 alpine并内置了一个轻量的 httpd 服务,已经相当的够用了。

按照作者的描述,简单使用如下命令就可以运行起来了。

docker run --restart=always -d
	-p 0.0.0.0:80:80 \
	--hostname=webdav \
	--name=webdav \
	-v /<host_directory_to_share>:/webdav \
	jgeusebroek/webdav

默认的账号密码是 webdav/vadbew。当然这样非常不安全。

以下是我的一些自定义配置,可以跟着我往下配置。

在这里我是重写了账号密码(debian系):

#!/bin/bash

apt-get install apache2-utils
htpasswd -c htpasswd kelu

我使用 htpasswd 命令创建了一个用户名为 kelu 的 htpasswd 文件,按照提示输入密码,便完成了认证文件的创建。创建一个config文件夹,并将它移入文件夹中:

mkdir config
mv htpasswd config
cd config
wget https://github.com/jgeusebroek/docker-webdav/raw/master/files/webdav.conf

最后我把默认的webdav的http配置也放到了配置文件夹中。

最后创建 docker-compose.yml 文件:

version: '3.2'

services:
  webdav:
    image: jgeusebroek/webdav
    container_name: webdav
    environment:
#      USER_UID: 0
      USER_GID: 50
      READWRITE: 'true'
    volumes:
      - /var/local/document:/webdav
      - ./config:/config
    ports:
      - '39989:80'
    restart: always

我将 /var/local/document 文件夹作为webdav的主目录,运行:

docker-compose up -d

便完成了服务的搭建,在 WebDAV 客户端中输入服务器/ip/端口/账号/密码即可访问。


1 2 3 4 5 6 7 89 90