第14章 用BERT实现中文语句分类


BERT以Transformer的Encoder为架构,已MLM为模型,在很多领域取得历史性的的突破。这里以Transformers上基于中文语料库上训练的预训练模型bert-base-chinese为模型,以BertForSequenceClassification为下游任务模型,在一个中文数据集上进行语句分类。具体包括如下内容:
 使用BERT的分词库Tokenizer
 可视化BERT注意力权重
 用BERT预训练模型微调下游任务
 训练模型

14.1 背景说明

本章用到预训练模型库Transformers,Transformers为自然语言理解(NLU)和自然语言生成(NLG)提供了最先进的通用架构(BERT、GPT、GPT-2、Transformer-XL、XLNET、XLM、T5等等),其中有超过32个100多种语言的预训练模型并同时支持TensorFlow 2.0和Pythorch1.0两大深度学习框架。可用pip安装Transformers。

Transformers的官网:https://github.com/huggingface
这章使用BERT模型中汉语版本:BERT-Base, Chinese: 包括简体和繁体汉字,共12层,768个隐单元,12个Attention head,110M参数。中文 BERT 的字典大小约有 2.1 万个标识符(tokens),这些预训练模型可以从Transformers官网下载。
使用了可视化工具BertViz,它的安装步骤如下:
1.下载bertviz:
https://github.com/jessevig/bertviz
2.解压到jupyter notebook当前目录下
bertviz-master

14.1.1 查看中文BERT字典里的一些信息

1.导入需要的库
指定使用预训练模型bert-base-chinese。

2. 查看tokenizer的信息

运行结果:
字典大小: 21128
3.查看分词的一些信息

运行结果:
token index
------------------------------
##san 10978
王 4374
##and 9369
蚀 6008
60 8183

BERT 使用当初 Google NMT 提出的 WordPiece Tokenization ,将本来的 words 拆成更小粒度的 wordpieces,有效处理不在字典里头的词汇 。中文的话大致上就像是 character-level tokenization,而有 ## 前缀的 tokens 即为 wordpieces。
除了一般的wordpieces以外,BERT还有5个特殊tokens:
 [CLS]:在做分类任务时其最后一层的表示.会被视为整个输入序列的表示;
 [SEP]:有两个句子的文本会被串接成一个输入序列,并在两句之间插入这个token作为分割;
 [UNK]:没出现在BERT字典里头的字会被这个token取代;
 [PAD]:zero padding掩码,将长度不一的输入序列补齐方便做batch运算;
 [MASK]:未知掩码,仅在预训练阶段会用到。

14.1.2 使用Tokenizer分割中文语句

让我们利用中文BERT的tokenizer将一个中文句子断词。

运行结果:
[CLS] 他移开这[MASK]桌子,就看到他的手表了。
['[CLS]', '他', '移', '开', '这', '[MASK]', '桌', '子', ',', '就'] ...
[101, 800, 4919, 2458, 6821, 103, 3430, 2094, 8024, 2218] ...

14.2 可视化BERT注意力权重

现在马上让我们看看给定上面有 [MASK] 的句子,BERT会填入什么字。

14.2.1 BERT对MAKS字的预测

运行结果:
輸入 tokens : ['[CLS]', '他', '移', '开', '这', '[MASK]', '桌', '子', ',', '就'] ...
--------------------------------------------------
Top 1 (83%):['[CLS]', '他', '移', '开', '这', '张', '桌', '子', ',', '就'] ...
Top 2 ( 7%):['[CLS]', '他', '移', '开', '这', '个', '桌', '子', ',', '就'] ...
Top 3 ( 0%):['[CLS]', '他', '移', '开', '这', '间', '桌', '子', ',', '就'] ...

BERT透过关注这桌这两个字,从2万多个wordpieces的可能性中选出"张"作为这个情境下[MASK] token的预测值,效果还是不错的。

14.2.2 导入可视化需要的库

1.导入需要的库

2.创建可视化使用html配置函数

14.2.3 可视化

运行结果:

图14-1 某词对其它词注意力权重示意图
这是BERT第 10 层 Encoder block 其中一个 head 的注意力结果,从改图可以看出,左边的这个他对右边的“宏”字关注度较高。

《自己动手做大数据系统》(第二版)演示程序安装使用手册

自己动手做大数据系统(第2版)
自己动手做大数据系统(第2版)

作者:飞谷云团队

编写时间:2020年2月(武汉加油、共克时艰)

交流QQ群:50926571

(镜像文件下载小程序)


  • 前言

为帮助读者更好地阅读和理解《自己动手做大数据系统》(第二版)中的实操部分,本文档主要讲解了如何使用Docker容器集群来运行书中的演示程序。容器集群共包括12个实例

镜像名称 功能 运行服务名
flink_single Flink单节点服务 StandaloneSessionClusterEntrypoint

TaskManagerRunner

hbase-master Hadoop、HBase、Spark集群主节点服务

HBaseThriftServer服务

SparkThriftServer服务

演示程序Flask服务

NameNode

SecondaryNameNode

HRegionServer

ThriftServer

Master

ResourceManager

SparkSubmit

HMaster

hbase-slave1 Hadoop、HBase、Spark集群从节点服务 HMaster --backup

HRegionServer

NodeManager

DataNode

CoarseGrainedExecutorBackend

Worker

hbase-slave2 Hadoop、HBase、Spark集群从节点服务

Hive metestore服务

上同hbase-slave1节点

多出来一个HiveMetaStore

kafka_single 使用本机ZooKeeper服务的单节点Kafka Broker服务 QuorumPeerMain

Kafka

mysql 存储Hive元数据和演示程序数据的MySQL数据库 mysqld
redis 单节点Redis数据库 redis-server
slave01 运行模拟站点2的Apache服务

收集本机web访问日志的flume agent服务

apache2

flume-ng

slave02 运行模拟站点1的Apache服务

收集本机web访问日志的flume agent服务

apache2

flume-ng

zoo1  

为HBase提供ZooKeeper服务的集群

QuorumPeerMain
zoo2 QuorumPeerMain
zoo3 QuorumPeerMain

 

注意:

  1. 阅读本文档假设你已经掌握了简单的Docker操作知识。
  2. 本镜像文件只是保证书中演示项目正常运行,并没有包含书中全部章节的演示代码内容。
  3. 本镜像文件中各个组件的配置文件不具有通用性,不要直接用于任何生产环境。
  4. 由于Docker容器的特性限制,集群中任何一个节点上新增或修改文件后,Docker容器重启后都会还原成初始状态。请特别注意!!
  5. 宿主机上的/root/feiguyun_book2/volume目录下的hadoop、hbase、mysql、spark、zk这5个目录不要做任何改动,不要删除这5个目录,也不要删除或添加目录中的任何内容。
  6. 宿主机上的/root/feiguyun_book2/volume/code目录映射到镜像节点hbase-master的/code目录,存放在code目录下的内容在Docker容器重启后不会丢失。

 

第一章 Docker环境安装和容器启动

一、选择宿主机操作系统

宿主机系统选用CentOS 6.10或7.x,或者是Ubuntu 18.04 LTS。本文档使用的是CentOS 7.6版本。在宿主机上统一使用root用户完成Docker镜像安装。以下命令查看OS版本号。

  • cat /etc/centos-release

CentOS Linux release 7.6.1810 (Core)

 

读者也可以在windows平台上先安装CentOS 的虚拟机,在虚拟机中运行Docker。本安装文档就是在虚拟机中测试完成的。无论使用实体机还是虚拟机作为宿主机,硬件资源最低需要硬盘为50G,内存为12G

二、修改yum源

由于在安装docker服务时会先安装一些前置工具,可以把CentOS的yum源改成国内的。具体方法可参考网上资料。(https://www.cnblogs.com/wdjsbk/p/9629931.html

 

三、在宿主机上安装Docker服务

依次运行以下命令

3-1. yum install -y yum-utils device-mapper-persistent-data lvm2

3-2. yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

3-3. yum makecache fast

3-4. rpm --import https://mirrors.aliyun.com/docker-ce/linux/centos/gpg

 

四、安装docker-ce服务

4-1. yum install docker-ce

4-2. usermod -aG docker $(whoami)

4-3. systemctl enable docker.service

4-4. systemctl start docker.service

 

五、在宿主机上安装docker-compose服务

5-1. yum install epel-release -y

5-2. yum install -y python-pip

5-3. python -m pip install --upgrade pip -i https://pypi.douban.com/simple

5-4. pip --version

pip 20.0.2 from /usr/lib/python2.7/site-packages/pip (python 2.7)

 

5-5. yum install python-devel

5-6. yum groupinstall "Development Tools"

5-7. yum install -y gcc g++ kernel-devel

 

5-8. pip install docker-compose -i https://pypi.douban.com/simple

 

最后查看docker-compose版本号

5-9. docker-compose -version

docker-compose version 1.25.4, build unknown

六、加载镜像文件和启动容器实例

6-1. 在/root目录下创建文件夹feiguyun_book2,把百度云盘下载的所有tar.gz文件放入该文件夹下。从读者QQ群文件中下载docker-compose.yml文件放入文件夹feiguyun_book2。把从读者QQ群文件中下载的code文件夹放入/root/feiguyun_book2/volume/目录下

 

6-2. 进入/root/feiguyun_book2目录,运行docker images命令,确保没有任何镜像文件;运行docker ps命令,确保没有任何镜像进程运行。如下图:

 

6-3. 加载镜像文件,在/root/feiguyun_book2目录下,依次运行以下命令

docker load < 1

docker load < 2

docker load < 3

docker load < 4

docker load < 5

docker load < 6

docker load < 7

docker load < 8

docker load < 9

注意:当镜像文件比较大时,加载文件会比较慢

 

6-4. 加载完成后,查看所有镜像文件列表

6-5.使用命令 docker network create hadoop-docker 创建网络

 

6-6. 创建mysql数据库表存放目录。在feiguyun_book2目录下运行以下两个命令行

mkdir volume/mysql

chown -R 999 volume/mysql/

 

6-7. docker-compose up -d 启动Docker镜像服务

6-7. docker-compose ps查看所有镜像进程启动情况,如下图所示

第二章 大数据环境初始化及启动、验证

一、格式化HDFS。只需要运行一次

在/root/feiguyun_book2目录下运行命令:

  • docker-compose exec hbase-master hdfs namenode -format

最后运行的结果如下所示

二、初始化Hive元数据库。只需要运行一次

Hive的元数据存放在mysql节点的数据库中,需要先创建可以连接mysql数据库的账户。在/root/feiguyun_book2目录下运行命令:

2-1. docker-compose exec mysql bash

进入到mysql 节点,使用cli工具进入数据库(下面红色部分是输入的命令内容)

root@mysql:/# mysql -u root -p

Enter password:  ******     <root用户的密码是hadoop>

进入后输入如下红色部分:

mysql> use mysql;

mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'hadoop' WITH GRANT OPTION;

mysql> FLUSH PRIVILEGES;

mysql> select host,user from user;

+-----------+---------------+

| host  | user     |

+-----------+---------------+

| %    | docker   |

| %     | root    |

| localhost | mysql.session |

| localhost | mysql.sys     |

| localhost | root   |

+-----------+---------------+

5 rows in set (0.00 sec)

 

 

运行完成,可以退出mysql 节点。

 

2-2. 在feiguyun_book2目录下输入以下命令,初始化hive数据库表数据

docker-compose exec hbase-master schematool -dbType mysql -initSchema

最后运行结果如下

三、启动Hadoop服务

在/root/feiguyun_book2目录下运行命令:

3-1. docker-compose exec hbase-master start-dfs.sh

3-2. docker-compose exec hbase-master start-yarn.sh

验证yarn服务和dfs服务

docker-compose exec hbase-master bash  输入命令进入hbase-master节点,在该节点中输入命令:

cd /root/hadoop/share/hadoop/mapreduce

3-4. hadoop jar hadoop-mapreduce-examples-2.7.7.jar pi 3 10

最后可以看到输出以下内容:

Estimated value of Pi is 3.60000000000000000000

 

 

四、启动Hive metastore服务

在/root/feiguyun_book2目录下运行命令:

docker-compose exec hbase-slave2 bash

进入hbase-slave2节点,启动服务,运行如下命令:

cd /root/hive/bin

4-1. nohup ./hive --service metastore&

 

启动后可以进入hive验证一下服务

在/root/feiguyun_book2目录下运行命令:

docker-compose exec hbase-master bash

进入hbase-master节点,启动hive cli,使用如下步骤:

[root@hbase-master /]# which hive

/root/hive/bin/hive

[root@hbase-master /]# hive

hive> show databases;

OK

default

Time taken: 1.767 seconds, Fetched: 1 row(s)

hive> exit;

 

五、启动HBase服务

在/root/feiguyun_book2目录下运行命令:

docker-compose exec hbase-master  bash

进入hbase-master节点,启动服务,运行如下命令:

cd /root/hbase/bin

5-1.  ./start-hbase.sh

启动后等待15秒左右,启动hbase shell,使用如下步骤:

[root@hbase-master /]# which hbase

/root/hbase/bin/hbase

[root@hbase-master /]# hbase shell

hbase(main):001:0> status

1 active master, 1 backup masters, 3 servers, 0 dead, 0.6667 average load

看到HBase节点状态都正常,顺便把两个表创建了,如下红色部分命令:

hbase(main):002:0> create 'rpt_artcl',{NAME=>'cf', VERSIONS=>3, COMPRESSION=>'GZ'}

0 row(s) in 5.7520 seconds

 

=> Hbase::Table - rpt_artcl

hbase(main):003:0> create 'access_log',{NAME=>'cf', VERSIONS=>3, COMPRESSION=>'GZ'}

0 row(s) in 2.4530 seconds

 

=> Hbase::Table - access_log

hbase(main):004:0> list

TABLE

access_log

rpt_artcl

2 row(s) in 0.0690 seconds

 

=> ["access_log", "rpt_artcl"]

最后使用exit退出HBase shell

【注】如果输入status命令显示ZooKeeper服务错误,请先查看文档最后“故障排除”中的解决办法。

 

在hbase-master节点启动Hbase thrift server服务

在/root/hbase/bin目录下运行命令

5-2.  sh  hbase-daemon.sh start thrift

 

六、启动Spark服务

在/root/feiguyun_book2目录下运行命令:

6-1. docker-compose exec hbase-master start-all.sh

启动后,登录hbase-slave2(或hbase-master、hbase-slave1)节点上验证spark服务

cd /root/spark/

6-2. ./bin/spark-submit --class org.apache.spark.examples.SparkPi --master spark://hbase-master.hadoop-docker:7077 examples/jars/spark-examples_2.11-2.4.1.jar 10

运行最后可以看到输出的Pi值如下,说明Spark正常使用

Pi is roughly 3.141452

 

七、启动SparkThriftServer服务

在/root/feiguyun_book2目录下运行命令:

docker-compose exec hbase-master bash 进入hbase-master节点后,运行下面红色命令

7-1. hdfs dfs -chmod -R  777 /tmp

7-2. cd /root/spark/sbin/

7-3. ./start-thriftserver.sh --hiveconf hive.server2.thrift.http.port=10000 --master spark://hbase-master.hadoop-docker:7077 --jars $HIVE_HOME/lib/hive-hbase-handler-2.3.4.jar,$HIVE_HOME/lib/hbase-client-1.4.9.jar,$HIVE_HOME/lib/hbase-common-1.4.9.jar,$HIVE_HOME/lib/hbase-server-1.4.9.jar,$HIVE_HOME/lib/hbase-protocol-1.4.9.jar,$HIVE_HOME/lib/guava-12.0.1.jar,$HIVE_HOME/lib/htrace-core-3.2.0-incubating.jar

 

验证sparkThriftServer服务

登录hbase-slave2(或hbase-master、hbase-slave1)节点,输入下面红色部分

[root@hbase-slave2 /]# cd /root/spark/bin

[root@hbase-slave2 bin]# ./beeline

Beeline version 1.2.1.spark2 by Apache Hive

beeline>  !connect jdbc:hive2://hbase-master:10000/

Connecting to jdbc:hive2://hbase-master:10000/

Enter username for jdbc:hive2://hbase-master:10000/: root

Enter password for jdbc:hive2://hbase-master:10000/: ****

(注意:这里的登录账户是运行 beeline的用户的用户名和密码, root用户的密码是root)

SLF4J: ... ...

Connected to: Spark SQL (version 2.4.1)

Driver: Hive JDBC (version 1.2.1.spark2)

Transaction isolation: TRANSACTION_REPEATABLE_READ

0: jdbc:hive2://hbase-master:10000/> show databases;

+-------------------------+--+

| databaseName  |

+-------------------------+--+

| default        |

+-------------------------+--+

1 row selected (2.507 seconds)

0: jdbc:hive2://hbase-master:10000/> !q

Closing: 0: jdbc:hive2://hbase-master:10000/

 

第三章 演示程序数据初始化及运行步骤

一、初始化Hive表

在/root/feiguyun_book2目录下运行命令:

docker-compose exec hbase-master bash 进入节点后,运行下面命令

  • hive -f /code/source-code/ch4/create-tables.hql

运行后,如果没有出错,可以进入beeline客户端查看所有创建的表,共10张。如下图所示:

 

二、运行数据导入程序

在/root/feiguyun_book2目录下运行命令:

docker-compose exec hbase-master bash 进入hbase-master节点后,运行下面命令

2-1.  cd /code/source-code/ch4/etl_job/

2-2.  chmod +x artcl_hive2hbase/hive2hbase.sh

2-3.  python3.6 run.py 20200220      ß参数20200220也可以改成其他任意日期

初始化脚本会运行大概7分钟,最终把数据插入到HBase中。以下是运行截图。

正常执行完毕后,可以进入HBase中查看记录数量,如下所示

hbase(main):001:0> count 'rpt_artcl'

90 row(s) in 1.7790 seconds

=> 90

 

运行导入数据程序只需执行一次,无需更换成其他日期重复运行,因为输入数据是固定的。

三、启动Flask前端查看数据

在hbase-master节点上运行下面命令

python36 /code/ch7/flask_app/run.py

 

运行后不要关闭该窗口。然后在宿主机上打开浏览器,输入http://宿主机IP地址:5000/index 比如我的宿主机IP地址是192.168.1.4,则可以输入 http://192.168.1.4:5000/index

进入演示程序后,可以进入“检索抓取文章”页面,输入时间段进行检索,如下所示

时间段请选择2017-5-1至2019-5-1之间的日期,其他日期段没有数据。

 

四、确认两个模拟网站正常访问

比如我的宿主机IP地址是192.168.1.4,则可以在浏览器中输入以下网址查看模拟新闻站点

http://192.168.1.4:10080/site1.com/

http://192.168.1.4:20080/site2.com/

 

五、创建kafka主题

进入宿主机/root/feiguyun_book2目录,输入以下命令

docker  exec -it kafka_single bash

进入后创建weblog主题,如下命令

5-1.  kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic weblog

5-2.  kafka-topics.sh --list --zookeeper localhost:2181

以下是命令运行截图

创建后退出该节点即可。

 

六、启动两个Flume Agent客户端

进入宿主机/root/feiguyun_book2目录,输入以下命令

docker-compose exec slave01 bash

进入slave01节点后,在根目录输入如下命令,启动后不要关闭窗口

6-1.  flume-ng agent --conf /opt/flume/conf/ --f /opt/weblog2kafka.conf  --name a1 -Dflume.root.logger=INFO,console

 

在宿主机中启动新的terminal,进入/root/feiguyun_book2目录

docker-compose exec slave02 bash

进入slave02节点后,在根目录输入如下命令,启动后不要关闭窗口

6-2.  flume-ng agent --conf /opt/flume/conf/ --f /opt/weblog2kafka.conf  --name a1 -Dflume.root.logger=INFO,console

 

七、启动Flink并加载job

容器中Flink服务默认自动启动,只需要打开浏览器查看WebUI即可,比如我的宿主机IP地址是192.168.1.4,则可以在浏览器中输入http://192.168.1.4:8081/

参照本书11.5章节介绍的步骤,把flink2hbase_docker.jar提交作业至Flink(flink2hbase_docker.jar文件在QQ群文件中可以获得),如下图所示

 

八、运行站点模拟访问程序

在宿主机上打开浏览器,输入http://宿主机IP地址:5000/ pvchart,比如我的宿主机IP地址是192.168.1.4,则可以输入 http://192.168.1.4:5000/pvchart

点击开始模拟按钮,会刷新图表20秒,如下图所示

演示完成后,可以到“检索访问日志”页面查看刚才模拟访问的页面地址,在http://192.168.1.4:5000/access_log 页面,如下所示。注意查询开始结束时间要选择刚才模拟访问的时间段,其他区间可能没有数据。

 

九、运行词云图生成和统计程序

根据刚才模拟访问的日志,可以生成关键字词云图,访问地址是 http://192.168.1.4:5000/wordcloud,注意查询开始结束时间要选择刚才模拟访问的时间段,如下所示:

注意:其中的英文单词是未被剔除的HTML标签,有兴趣的读者可以完善自己的stopwords停用词典。如下图是生成访问关键字占比统计,时间段同样要选择刚才模拟访问的20秒。

第四章 如何关闭服务

一、关闭大数据组件服务步骤

离线大数据组件主要安装在hbase-master节点上,下面的操作都在hbase-master上运行:

cd /root/hbase/bin

./hbase-daemon.sh stop thrift

./stop-hbase.sh

 

cd /root/spark/sbin

./stop-thriftserver.sh

./stop-all.sh

 

cd /root/hadoop/sbin

./stop-yarn.sh

./stop-dfs.sh

 

流式大数据组件的服务不用手动关闭。

 

二、关闭容器集群

确认关闭hbase-master上的服务后,可以进入宿主机关闭容器集群服务。

在/root/feiguyun_book2目录下运行命令:

docker-compose down  如下所示

注意事项:

关闭容器集群后如果要再次启动,只需要在/root/feiguyun_book2目录下执行

docker-compose up -d

启动命令即可,不需要再次执行格式化HDFS和初始化Hive元数据库。

 

 

“非习劳无以进业”---- 希望您能通过以上步骤的指引,顺利运行演示程序,并最终学有所获!!

 

(完)

附录 故障排除

一、Hadoop启动失败

Hadoop集群启动后,可以分别登录hbase-master,hbase-slave1,hbase-slave2三个节点,依次查看Java进程是否正确。如果HDFS或MapReduce无法正常运行,可在hbase-master节点手动关闭dfs和yarn服务,尝试再次启动。

 

二、HBase启动失败

在虚拟机作为宿主机的场景下,经常会出现HBase首次启动失败,hbase-master节点上的HMaster进程启动后自动消失,进入hbase shell客户端使用status查看状态会提示ZooKeeper服务出错。

出现这种错误的原因是由于系统资源紧张,ZooKeeper集群和HBase通讯出故障。zoo1,zoo2,zoo3三个节点的ZooKeeper服务都是随着Docker服务自动启动的,不必手动运行。当HBase服务启动失败时,可以先进入hbase-slave1和hbase-slave2节点,使用kill命令杀掉HRegionServer服务,最后进入hbase-master节点,运行:

cd /root/hbase/bin/

./stop-hbase.sh

关闭HBase服务,再使用 ./start-hbase.sh 启动,往往就正常了。

三、Spark启动失败

Spark启动失败的情况比较少见,可以尝试关闭后再启动。或者可以修改hbase-master节点上Spark启动脚本里面内存使用量,由于本演示程序只使用Spark作为Hive的计算引擎这一个特性,可以把内存使用量调小,比如定义成256M。

四、初始化Hive表错误

Hive表DDL都定义在create-tables.hql文件中,只需要运行一次。如果第一次运行失败,再次运行前,要修改create-tables.hql文件,注释掉 create database feigu 这行。

五、运行数据导入程序错误

运行该步骤前要确保Hive metastore和HBase服务及SparkThriftServer服务都已正常启动,否则会报错。

六、运行站点模拟访问程序错误

点击开始模拟按钮后会出现坐标轴,但没有生成折线。出现这种错误要依次排查3处:

 

  • 模拟新闻站点运行是否正常

打开浏览器,访问slave01或slave02的新闻站点,同时进入slave01或slave02的终端,运行 tail -f /var/log/apache2/access.log 看是否有日志输出。

 

  • Kafka中weblog主题是否正常接收到数据

如果flume-ng可以收到日志数据,则进入kafka_single节点的终端,启动一个消费者

kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic weblog --from-beginning

来订阅weblog主题的数据,看是否有数据输出

 

  • Flink作业是否运行正常

如果kafka主题可以接收到数据,要排查Flink作业日志输出,在WebUI上查看TaskManager的Logs中是否有异常抛出,如下所示

然后再查看Stdout页面中是否打印出如下内容

七、其他问题

可以在QQ群50926571中提问,或是在feiguyunai上发帖求助。

 

 

如何用PyTorch加载自己的数据集?


数据预处理在解决深度学习问题的过程中,往往需要花费大量的时间和精力。 数据处理的质量对训练神经网络来说十分重要,良好的数据处理不仅会加速模型训练, 更会提高模型性能。为解决这一问题,PyTorch提供了几个高效便捷的工具, 以便使用者进行数据处理或增强等操作,同时可通过并行化加速数据加载。
【说明】本文使用的cat-dog数据集,请到书《Python深度学习基于PyTorch》下载代码及数据部分下载。
数据集存放大致有以下两种方式:
(1)所有数据集放在一个目录下,文件名上附有标签名,数据集存放格式如下: root/cat_dog/cat.01.jpg
root/cat_dog/cat.02.jpg
........................
root/cat_dog/dog.01.jpg
root/cat_dog/dog.02.jpg
......................
(2)不同类别的数据集放在不同目录下,目录名就是标签,数据集存放格式如下:
root/ants/xxx.png
root/ants/xxy.jpeg
root/ants/xxz.png
................
root/bees/123.jpg
root/bees/nsdf3.png
root/bees/asd932_.png
..................

1.1 对第1种数据集的处理步骤

(1)生成包含各文件名的列表(List)
(2)定义Dataset的一个子类,该子类需要继承Dataset类,查看Dataset类的源码
(3)重写父类Dataset中的两个魔法方法: 一个是: __lent__(self),其功能是len(Dataset),返回Dataset的样本数。 另一个是__getitem__(self,index),其功能假设索引为i,使Dataset[i]返回第i个样本。
(4)使用torch.utils.data.DataLoader加载数据集Dataset.

1.2 实例详解

以下以cat-dog数据集为例,说明如何实现自定义数据集的加载。

1.2.1 数据集结构

所有数据集在cat-dog目录下:
.\cat_dog\cat.01.jpg
.\cat_dog\cat.02.jpg
.\cat_dog\cat.03.jpg
....................
.\cat_dog\dog.01.jpg
.\cat_dog\dog.02.jpg
....................

1.2.2 导入需要用到的模块

1.2.3定义加载自定义数据的类

1.2.4 实例化类

<class 'PIL.Image.Image'>

1.2.5 查看图像形状

img的形状(500, 374),label的值0
img的形状(300, 280),label的值0
img的形状(489, 499),label的值0
img的形状(431, 410),label的值0
img的形状(300, 224),label的值0

从上面返回样本的形状来看:
(1)每张图片的大小不一样,如果需要取batch训练的神经网络来说很不友好。
(2)返回样本的数值较大,未归一化至[-1, 1]
为此需要对img进行转换,如何转换?只要使用torchvision中的transforms即可

1.2.6 对图像数据进行处理

这里使用torchvision中的transforms模块

1.2.7查看处理后的数据

图像img的形状torch.Size([3, 224, 224]),标签label的值0
图像数据预处理后:
tensor([[[ 0.9059, 0.9137, 0.9137, ..., 0.9451, 0.9451, 0.9451],
[ 0.9059, 0.9137, 0.9137, ..., 0.9451, 0.9451, 0.9451],
[ 0.9059, 0.9137, 0.9137, ..., 0.9529, 0.9529, 0.9529],
...,
[-0.4824, -0.5294, -0.5373, ..., -0.9216, -0.9294, -0.9451],
[-0.4980, -0.5529, -0.5608, ..., -0.9294, -0.9373, -0.9529],
[-0.4980, -0.5529, -0.5686, ..., -0.9529, -0.9608, -0.9608]],

[[ 0.5686, 0.5765, 0.5765, ..., 0.7961, 0.7882, 0.7882],
[ 0.5686, 0.5765, 0.5765, ..., 0.7961, 0.7882, 0.7882],
[ 0.5686, 0.5765, 0.5765, ..., 0.8039, 0.7961, 0.7961],
...,
[-0.6078, -0.6471, -0.6549, ..., -0.9137, -0.9216, -0.9373],
[-0.6157, -0.6706, -0.6784, ..., -0.9216, -0.9294, -0.9451],
[-0.6157, -0.6706, -0.6863, ..., -0.9451, -0.9529, -0.9529]],

[[-0.0510, -0.0431, -0.0431, ..., 0.2078, 0.2157, 0.2157],
[-0.0510, -0.0431, -0.0431, ..., 0.2078, 0.2157, 0.2157],
[-0.0510, -0.0431, -0.0431, ..., 0.2157, 0.2235, 0.2235],
...,
[-0.9529, -0.9843, -0.9922, ..., -0.9529, -0.9608, -0.9765],
[-0.9686, -0.9922, -1.0000, ..., -0.9608, -0.9686, -0.9843],
[-0.9686, -0.9922, -1.0000, ..., -0.9843, -0.9922, -0.9922]]])

由此可知,数据已标准化、规范化。

1.2.8对数据集进行批量加载

使用DataLoader模块,对数据集dataset进行批量加载

torch.Size([4, 3, 224, 224]) torch.Size([4])
torch.Size([4, 3, 224, 224]) torch.Size([4])
torch.Size([4, 3, 224, 224]) torch.Size([4])
torch.Size([4, 3, 224, 224]) torch.Size([4])
torch.Size([4, 3, 224, 224]) torch.Size([4])
torch.Size([4, 3, 224, 224]) torch.Size([4])
torch.Size([4, 3, 224, 224]) torch.Size([4])
torch.Size([4, 3, 224, 224]) torch.Size([4])
torch.Size([4, 3, 224, 224]) torch.Size([4])
torch.Size([4, 3, 224, 224]) torch.Size([4])
torch.Size([2, 3, 224, 224]) torch.Size([2])

1.2.9随机查看一个批次的图像

2 对第2种数据集的处理

处理这种情况比较简单,可分为2步:
(1)使用datasets.ImageFolder读取、处理图像。
(2)使用.data.DataLoader批量加载数据集,示例如下:

Embedding新时代

6.1 概述

从2013 google推出最初的一种Word Embedding(即word2vec)后,其发展非常迅速,尤其近些年对Word Embedding的应用和拓展,取得了非常大的进步。
Embedding的起源,应该追溯到Word2Vec,Word2Vec是一种Word Embedding。由于Word Embedding克服了传统整数向量、One-Hot向量的耗资源、对语句表现能力差等不足,Word Embedding在最近几年发展和创新不断,从最初的Word Embedding,已发展成Sequence Embedding、catategorial data Embedding、Graph Embedding等等。应用也从当初的自然语言处理(NLP)到传统机器学习、自然语言处理、推荐系统、广告、搜索排序等领域,并大大提升了这些应用的性能。
Embedding尤其在NLP方面,近些年取得巨大进步。可以说是最近几年在NLP领域最大的突破就在Word Embedding方面。如将替换RNN\LSTM的Transformer或Transformer-XL,在很多下游复杂任务上已超越人类平均水平的BERT、XLNET模型等等。而这些算法或模型的应用开始在推荐、传统机器学习、视觉处理、NLP方面开始爆发!难怪有人说Embedding时代已来临!
以下我用图形这种简单明了的方式,简单介绍一下Embedding的历史及最近几年的突破性进展,希望能给大家进一步学习提供参考。

6.2Embedding在Entity领域的拓展

Entity Embedding是推荐系统、计算广告领域最近非常流行的做法,是从word2vec等一路发展而来的Embedding技术的最新延伸;
Entity Embedding中的Categorical Variable Embedding已成为贯通传统机器学习与深度学习的桥梁。

6.3 Embedding在NLP领域的最新进展

贪心学院2020-2-15至2020-2-22日5次公开课

6.4 在Graph领域的拓展

Graph Embedding是推荐系统、计算广告领域最近非常流行的做法,是从word2vec等一路发展而来的Embedding技术的最新延伸;
Graph Embedding已经有很多大厂将Graph Embedding应用于实践后取得了非常不错的线上效果。

6.5 Embedding 应用到机器学习中

Embedding的起源和火爆都是在NLP中的,经典的word2vec都是在做word embedding这件事情,而真正首先在结构数据探索embedding的是在kaggle上的《Rossmann Store Sales》中的用简单特征工程取得第3名的解决方案(前两名为该领域的专业人士,采用了非常复杂的特征工程),作者在比赛完后为此方法整理一篇论文放在了arXiv,文章名:《Entity Embeddings of Categorical Variables》。

6.6Embedding在推荐领域的超级应用

1、[Item2Vec] Item2Vec-Neural Item Embedding for Collaborative Filtering (Microsoft 2016)
这篇论文是微软将word2vec应用于推荐领域的一篇实用性很强的文章。该文的方法简单易用,可以说极大拓展了word2vec的应用范围,使其从NLP领域直接扩展到推荐、广告、搜索等任何可以生成sequence的领域。
2、[Airbnb Embedding] Real-time Personalization using Embeddings for Search Ranking at Airbnb (Airbnb 2018)
Airbnb的这篇论文是KDD 2018的best paper,在工程领域的影响力很大,也已经有很多人对其进行了解读。简单来说,Airbnb对其用户和房源进行embedding之后,将其应用于搜索推荐系统,获得了实效性和准确度的较大提升。文中的重点在于embedding方法与业务模式的结合,可以说是一篇应用word2vec思想于公司业务的典范。
3、 [Airbnb Embedding] Real-time Personalization using Embeddings for Search Ranking at Airbnb (Airbnb 2018)
Airbnb的这篇论文是KDD 2018的best paper,在工程领域的影响力很大,也已经有很多人对其进行了解读。简单来说,Airbnb对其用户和房源进行embedding之后,将其应用于搜索推荐系统,获得了实效性和准确度的较大提升。文中的重点在于embedding方法与业务模式的结合,可以说是一篇应用word2vec思想于公司业务的典范。
4、[Alibaba Embedding] Billion-scale Commodity Embedding for E-commerce Recommendation in Alibaba (Alibaba 2018)
阿里巴巴在KDD 2018上发表的这篇论文是对Graph Embedding非常成功的应用。从中可以非常明显的看出从一个原型模型出发,在实践中逐渐改造,最终实现其工程目标的过程。这个原型模型就是上面提到的DeepWalk,阿里通过引入side information解决embedding问题非常棘手的冷启动问题,并针对不同side information进行了进一步的改造形成了最终的解决方案。
5、Behavior Sequence Transformer for E-commerce Recommendation in Alibaba(阿里2019)
近日,阿里巴巴搜索推荐事业部发布了一项新研究,首次使用强大的 Transformer 模型捕获用户行为序列的序列信号,供电子商务场景的推荐系统使用。该模型已经部署在淘宝线上,实验结果表明,与两个基准线对比,在线点击率(CTR)均有显著提高。

6.7 Embedding协助NLP极大提升性能

从简单的 Word2Vec,ELMo,GPT,BERT,XLNet到ALBERT, 这几乎是NLP过去10年最为颠覆性的成果。
1、BERT
BERT是一种基于Transformer Encoder来构建的一种模型,它整个的架构其实是基于DAE(Denoising Autoencoder)的,这部分在BERT文章里叫作Masked Lanauge Model(MLM)。MLM并不是严格意义上的语言模型,因为整个训练过程并不是利用语言模型方式来训练的。BERT随机把一些单词通过MASK标签来代替,并接着去预测被MASK的这个单词,过程其实就是DAE的过程。BERT有两种主要训练好的模型,分别是BERT-Small和BERT-Large, 其中BERT-Large使用了12层的Encoder结构。 整个的模型具有非常多的参数。
BERT在2018年提出,当时引起了爆炸式的反应,因为从效果上来讲刷新了非常多的记录,之后基本上开启了这个领域的飞速的发展。
2、XLNET
XLNet震惊了NLP领域,这种语言建模的新方法在20个NLP任务上的表现优于强大的BERT,并且在18个任务中获得了最先进的结果。
3、ALBERT
谷歌Lab近日发布了一个新的预训练模型"ALBERT"全面在SQuAD 2.0、GLUE、RACE等任务上超越了BERT、XLNet再次刷新了排行榜!ALBERT是一种轻量版本的BERT,利用更好的参数来训练模型,但是效果却反而得到了很大提升!ALBERT的核心思想是采用了两种减少模型参数的方法,比BERT占用的内存空间小很多,同时极大提升了训练速度,更重要的是效果上也有很大的提升!

神经网络如何更有效地实现传统机器学习任务?


在《XGBoost与神经网络谁更牛?》文章,基于相同数据,对XGBoost和神经网络(NN)两种方法进行横行比较。XGBoost是属于Tree Model,只把分类特征进行数字化。对神经网络(NN)先把分类特征数值化,然后转换为One-hot。结果两个算法的测试结果(损失值)如下:
表3-1两种不同算法测试结果

《XGBoost与神经网络谁更牛?》基于相同数据集用不同算法进行比较,本章将从纵向进行比较,即基于相同数据集,相同算法(都是神经网络NN),但特征处理方式不同。一种是《XGBoost与神经网络谁更牛?》的方法,另外一种对分类特征采用Embedding方法,把分类特征转换为向量,并且这些向量的值会随着模型的迭代而自动更新。这两种方法比较的结果如表3-2所示:
表3-2 同一算法不同特征处理方式的测试结果

训练及测试数据没有打乱,其中测试数据是最新原数据的10%。
从表3-2可以看出,使用EE处理分类特征的方法性能远好于不使用EE的神经网络,使用EE有利于充分发挥NN的潜能,提升NN在实现传统机器学习任务的性能。
EE处理方法为何能大大提升模型的性能?使用EE处理特征有哪些优势,Embedding方法是否还适合于处理其它特征?如连续特征、时许特征、图(graph)特征等。这些问题后续将陆续进行说明。接下来我们将用Keras或TensorFlow的Embeding层处理分类特征。

3.1 Embedding简介

神经网络、深度学习的应用越来越广泛,从计算机视觉到自然语言处理和时间序列预测。在这些领域都取得不错成绩,有很多落地项目性能已超过人的平均水平。不但有较好的性能,特征工程方面更是实现了特征工程的自动化。
特征工程是传统机器学习的核心任务,模型性能如何很大程度取决特征工程处理方法,由此,要得到理想的特征工程往往需要很多业务和技术方面的技巧,因此有“特征工程是一门艺术”的说法,这也从一个侧面说明特征工程的门槛比较高,不利于普及和推广。不过这种情况,近些年正在改变。为了解决这一大瓶颈,人们开始使用神经网络或深度学习方法处理传统机器学习任务,特征工程方法使用Embedding 方法,把离散变量转变为较低维的向量,通过这种方式,我们可以将神经网络,深度学习用于更广泛的领域。
目前Embedding已广泛使用在自然语言处理(NLP)、结构化数据、图形数据等处理方面。在NLP 中常用的 Word Embedding ,对结构化数据使用 Entity Embedding,对图形数据采用Graph Embedding。这些内容后续我们将陆续介绍。这里先介绍如何用keras或TensorFlow实现分类特征的Embedding。

3.1.1 Keras.Embedding格式

keras的Embedding层格式如下:

Keras提供了一个嵌入层(Embedding layer),可用于处理文本数据、分类数据等的神经网络。它要求输入数据进行整数编码,以便每个单词或类别由唯一的整数表示。嵌入层使用随机权重初始化,并将学习所有数据集中词或类别的表示。这层只能作为模型的第1层。
【参数说明】
 input_dim: int > 0。词汇表大小或类别总数, 即,最大整数索引(index) + 1。
 output_dim: int >= 0。词向量的维度。
 embeddings_initializer: embeddings 矩阵的初始化方法 (详见 https://keras.io/initializers/)。
 embeddings_regularizer: embeddings matrix 的正则化方法
(详见https://keras.io/regularizers/)。
 embeddings_constraint: embeddings matrix 的约束函数 (详见 https://keras.io/constraints/)。
 mask_zero: 是否把 0 看作为一个应该被遮蔽的特殊的 "padding" 值。这对于可变长的循环神经网络层十分有用。 如果设定为 True,那么接下来的所有层都必须支持 masking,否则就会抛出异常。 如果 mask_zero 为 True,作为结果,索引 0 就不能被用于词汇表中 (input_dim 应该与 vocabulary + 1 大小相同)。
 input_length: 输入序列的长度,当它是固定的时。 如果你需要连接 Flatten 和 Dense 层,则这个参数是必须的 (没有它,dense 层的输出尺寸就无法计算)。
 输入尺寸
尺寸为 (batch_size, sequence_length) 的 2D 张量。
 输出尺寸
尺寸为 (batch_size, sequence_length, output_dim) 的 3D 张量。
更多信息可参考官网:
https://keras.io/layers/embeddings/
https://keras.io/zh/layers/embeddings/(中文)
假设定义一个词汇量为200的嵌入层的整数编码单词,将词嵌入到32维的向量空间,每次输入50个单词的输入文档,那么对应的embedding可写成如下格式:

为更好理解Keras的Embedding层的使用,下面列举几个具体实例。
(1)简单实例代码:

(2)用Embedding学习文本表示实例
假设有10个文本文档,每个文档都有一个学生提交的工作评论。每个文本文档被分类为正的“1”或负的“0”。这是一个简单的情感分析问题。用Keras的Embedding学习这10个文本的表示,具体实现代码如下;
#导入需要的模块

运行结果如下:
把文本转换为整数
[[49, 28], [33, 36], [21, 41], [18, 36], [32], [29], [18, 41], [30, 33], [18, 36], [43, 17, 28, 34]]
填充向量
[[49 28 0 0]
[33 36 0 0]
[21 41 0 0]
[18 36 0 0]
[32 0 0 0]
[29 0 0 0]
[18 41 0 0]
[30 33 0 0]
[18 36 0 0]
[43 17 28 34]]
查看模型结构
Model: "sequential_5"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_5 (Embedding) (None, 4, 8) 400
_________________________________________________________________
flatten_4 (Flatten) (None, 32) 0
_________________________________________________________________
dense_4 (Dense) (None, 1) 33
=================================================================
Total params: 433
Trainable params: 433
Non-trainable params: 0
_________________________________________________________________
None
查看模型精度
Accuracy: 89.999998

查看通过50次迭代后的Embedding矩阵。

运行结果如下:
array([[ 0.03469506, 0.05556902, -0.06460979, 0.04944995, -0.04956526,
-0.01446372, -0.01657126, 0.04287368],
[ 0.04969586, -0.0284451 , -0.03200825, -0.00149088, 0.04212971,
-0.00741715, -0.02147427, -0.02345204],
[ 0.00152697, 0.04381416, 0.01856637, -0.00952369, 0.04007444,
0.00964203, -0.0313913 , -0.04820969]], dtype=float32)

3.1.2 Dense简介

Dense就是常用的的全连接层,它实现以下操作:

其中 activation 是按逐个元素计算的激活函数,kernel 是由网络层创建的权值矩阵,以及 bias 是其创建的偏置向量 (只在 use_bias 为 True 时才有用)。
注意: 如果该层的输入的秩大于2,那么它首先被展平然后 再计算与 kernel 的点乘。
【参数说明】
 units: 正整数,输出空间维度。
 activation: 激活函数 (详见 activations)。 若不指定,则不使用激活函数 (即,「线性」激活: a(x) = x)。
 use_bias: 布尔值,该层是否使用偏置向量。
 kernel_initializer: kernel 权值矩阵的初始化器 (详见https://keras.io/zh/initializers/)。
 bias_initializer: 偏置向量的初始化器 (详见https://keras.io/zh/initializers/).
 kernel_regularizer: 运用到 kernel 权值矩阵的正则化函数 (详见 https://keras.io/zh/regularizers/)。
 bias_regularizer: 运用到偏置向的的正则化函数 (详见 https://keras.io/zh/regularizers/)。
 activity_regularizer: 运用到层的输出的正则化函数 (它的 "activation")。 (详见 https://keras.io/zh/regularizers/)。
 kernel_constraint: 运用到 kernel 权值矩阵的约束函数 (详见https://keras.io/zh/constraints/)。
 bias_constraint: 运用到偏置向量的约束函数 (详见https://keras.io/zh/constraints/)。
 输入尺寸
nD张量,尺寸: (batch_size, ..., input_dim)。 最常见的情况是一个尺寸为 (batch_size, input_dim) 的2D输入。
 输出尺寸
nD张量,尺寸: (batch_size, ..., units)。 例如,对于尺寸为 (batch_size, input_dim)的2D输入, 输出的尺寸为 (batch_size, units)。

简单示例代码:

3.2 NN架构


图3-1 NN架构图
从图3-1可知,NN共处理7个特征,其中promo特征因只有2个值(0或1),无需转换为Embedding向量,其它6个特征,根据类别数,分别做了Embedding处理。处理后合并这7个特征,再通过3个全连接层。

3.3 分类特征处理

基于第2章(《XGBoost与NN谁更牛?》)处理保存的feature_train_data.pickle文件,做如下处理:

3.3.1 数据预处理

(1)定义对特征进行Embedding处理函数

(2)导入模块

(3)读取数据

(4)生成训练、测试数据

(5)定义采样函数

(6)采样生成训练数据

3.3.2 构建模型

(1)定义Model类

(2)构建模型

(3)训练模型

运行部分结果
Fitting NN_with_EntityEmbedding...
Train on 200000 samples, validate on 84434 samples
Epoch 1/10
200000/200000 [==============================] - 37s 185us/sample - loss: 0.0140 - val_loss: 0.0113
Epoch 2/10
200000/200000 [==============================] - 33s 165us/sample - loss: 0.0093 - val_loss: 0.0110
Epoch 3/10
200000/200000 [==============================] - 34s 168us/sample - loss: 0.0085 - val_loss: 0.0104
Epoch 4/10
200000/200000 [==============================] - 35s 173us/sample - loss: 0.0079 - val_loss: 0.0107
Epoch 5/10
200000/200000 [==============================] - 37s 184us/sample - loss: 0.0076 - val_loss: 0.0100
Epoch 6/10
200000/200000 [==============================] - 38s 191us/sample - loss: 0.0074 - val_loss: 0.0095
Epoch 7/10
200000/200000 [==============================] - 31s 154us/sample - loss: 0.0072 - val_loss: 0.0097
Epoch 8/10
200000/200000 [==============================] - 33s 167us/sample - loss: 0.0071 - val_loss: 0.0091
Epoch 9/10
200000/200000 [==============================] - 36s 181us/sample - loss: 0.0069 - val_loss: 0.0090
Epoch 10/10
200000/200000 [==============================] - 40s 201us/sample - loss: 0.0068 - val_loss: 0.0089
Result on validation data: 0.09481584162850512
Train on 200000 samples, validate on 84434 samples
Epoch 1/10
200000/200000 [==============================] - 38s 191us/sample - loss: 0.0143 - val_loss: 0.0125
Epoch 2/10
200000/200000 [==============================] - 41s 206us/sample - loss: 0.0096 - val_loss: 0.0107
Epoch 3/10
200000/200000 [==============================] - 46s 232us/sample - loss: 0.0089 - val_loss: 0.0105
Epoch 4/10
200000/200000 [==============================] - 39s 197us/sample - loss: 0.0082 - val_loss: 0.0099
Epoch 5/10
200000/200000 [==============================] - 39s 197us/sample - loss: 0.0077 - val_loss: 0.0095
Epoch 6/10
200000/200000 [==============================] - 41s 207us/sample - loss: 0.0075 - val_loss: 0.0111
Epoch 7/10
200000/200000 [==============================] - 39s 193us/sample - loss: 0.0073 - val_loss: 0.0092
Epoch 8/10
200000/200000 [==============================] - 50s 248us/sample - loss: 0.0071 - val_loss: 0.0092
Epoch 9/10
200000/200000 [==============================] - 46s 228us/sample - loss: 0.0070 - val_loss: 0.0094
Epoch 10/10
200000/200000 [==============================] - 44s 221us/sample - loss: 0.0069 - val_loss: 0.0091
Result on validation data: 0.09585602861091462

3.3.3 验证模型

运行结果如下:
Evaluate combined models...
Training error...
0.06760082089742254
Validation error...
0.09348419043167332

3.4 可视化Entity Embedding

把特征转换为Entity Embedding之后,可以利用t-SNE进行可视化,如对store特征的Embedding降维后进行可视化,从可视化结果揭示出一些重要信息,彼此相似的类别比较接近。

3.4.1 保存Embedding

3.4.2 可视化Embedding特征

(1)导入模块

(2)读取保存的embedding文件

(3)定义对应各州的名称

(4)可视化german_states_embedding

可视化结果如下:


图3-2 可视化german_states_embedding
从图3-2 可知,德国的原属于东德的几个州:萨克森州、萨克森安哈尔特州、图林根州彼此比较接近。其它各州也有类似属性,这就是Embedding通过多次迭代,从数据中学习到一些规则。

XGBoost与神经网络谁更牛?


XGBoost是Kaggle上的比赛神器,近些年在kaggle或天池比赛上时常能斩获大奖,不过这样的历史正在改变!最近几年神经网络的优势开始从非结构数据向结构数据延伸,而且在一些Kaggle比赛中取得非常不错的成绩。
XGBoost很牛,不过更牛的应该是NN! 这章我们通过一个实例来说明,本实例基于相同数据,使用XGBoost和神经网络(NN)对类别数据转换为数字。XGBoost是属于Tree Model,故是否使用One-hot影响不大(通过测试,确实如此,相反,如果转换为one-hot将大大增加内存开销),所以使用xgboost的数据转换为数字后,没有再转换为one-hot编码。对神经网络(NN)而言,是否把数据转换为One-hot,影响比较大,所以使用NN的模型数据已转换为One-hot。
两种算法测试结果为表2-1。

训练及测试数据没有打乱,其中测试数据是最新原数据的10%。

2.1 XGBoost简介

2.1.1概述

XGBoost的全称是eXtreme Gradient Boosting,由很多CART(Classification And Regression Tree)树集成,其中CART是对分类决策树和回归决策树的总称。
分类决策树一般使用信息增益、信息增益率、基尼系数来选择特征的依据。CART回归树是假设树为二叉树,通过不断将特征进行分裂。比如当前树结点是基于第j个特征值进行分裂的,设该特征值小于s的样本划分为左子树,大于s的样本划分为右子树。因此,当我们为了求解最优的切分特征j和最优的切分点s,就转化为求解这么一个目标函数。

只要遍历所有特征的的所有切分点,就能找到最优的切分特征和切分点。最终得到一棵回归树。

2.1.2 主要原理

XGBoost本质上还是一个GBDT(Gradient Boosting Decision Tree),但为力争把速度和效率发挥到极致,所以叫X (Extreme) GBoosted。GBDT的原理就是所有弱分类器的结果相加等于预测值,然后下一个弱分类器去拟合误差函数对预测值的梯度(或残差)(这个梯度/残差就是预测值与真实值之间的误差)。一个弱分类器如何去拟合误差函数残差?
举一个非常简单的例子,比如我今年30岁了,但计算机或者模型GBDT并不知道我今年多少岁,那GBDT咋办呢?
(1)它会在第一个弱分类器(或第一棵树中)随便用一个年龄比如20岁来拟合,然后发现误差有10岁;
(2)在第二棵树中,用6岁去拟合剩下的损失,发现差距还有4岁;
(3)在第三棵树中用3岁拟合剩下的差距,发现差距只有1岁了;
(4)在第四课树中用1岁拟合剩下的残差,完美。
最终,四棵树的结论加起来,就是真实年龄30岁。实际工程中GBDT是计算负梯度,用负梯度近似残差。
注意,为何GBDT可以用用负梯度近似残差呢?
回归任务下,GBDT 在每一轮的迭代时对每个样本都会有一个预测值,此时的损失函数为均方差损失函数,表达式如下:

那此时的负梯度是这样计算的,具体表达式如下:

所以,当损失函数选用均方损失函数是时,每一次拟合的值就是(真实值 - 当前模型预测的值),即残差。此时的变量是,即“当前预测模型的值”,也就是对它求负梯度。
更多详细内容可参考:https://blog.csdn.net/v_july_v/article/details/81410574

2.1.3 主要优点

(1)目标表达式:
XGBoost优化了GBDT的目标函数。一方面,在GBDT的基础上加入了正则项,包括叶子节点的个数和每个叶子节点输出的L2模的平方和,正则项可以控制树的复杂度,让模型倾向于学习简单的模型,防止过拟合;另外,XGBoost还支持线性分类器,传统的GBDT是以CART算法作为基学习器。
(2)使用Shrinkage:
对每棵树的预测结果采用了shrinkage,相当于学习率,降低模型对单颗树的依赖,提升模型的泛化能力。
(3)采用列采样:
XGBoost借助了随机森林的优点,采用了列采样,进一步防止过拟合,加速训练过程,而传统的GBDT则没有列采样。
(4)优化方法:
XGBoost对损失函数的展开采用了一阶梯度和二阶梯度,而传统的GBDT只采用了一阶梯度。
(5)增益计算:
对分裂依据进行了优化。不同于CART算法,XGBoost采用了新的基于一阶导数和二阶导数的统计信息作为树的结构分数,采用分列前的结构与分裂后的结构得分的增益作为分裂依据,选择增益最大的特征值作为分裂点,替代了回归树的误差平方和。
(6)最佳增益节点查找:
XGBoost在寻找最佳分离点的方式上采用了近似算法,基于权重的分位数划分方式(权重为二阶梯度)。主要是对特征取值进行分桶,然后基于桶的值计算增益。
(7)预排序。
在寻找最佳增益节点时,将所有数据放入内存进行计算,得到预排序结果,然后在计算分裂增益的时候直接调用。
(8)缺失值处理
对于特征的值有缺失的样本,Xgboost可以自动学习出他的分裂方向。Xgboost内置处理缺失值的规则。
(9)支持并行。
众所周知,Boosting算法是顺序处理的,也是说Boosting不是一种串行的结构吗?怎么并行的?注意XGBoost的并行不是tree粒度的并行。XGBoost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含)。XGBoost的并行式在特征粒度上的,也就是说每一颗树的构造都依赖于前一颗树。

2.1.4 XGBoost的模型参数

XGBoost使用字典的方式存储参数,主要参数有如下这些:

安装XGBoost建议使用conda命令。如:conda install py-xgboost=0.90

2.2 NN简介

使用的神经网络结构如下图所示

图2-1 神经网络结构
本实例使用的神经网络结构比较简单,共4层,除输入、输出层外,中间是两个全连接层。输入层1183个节点,这个正好是特征转换为one-hot后的元素个数,第1个隐含层的节点是1000,激活函数为relu,第2个隐含层的节点数为500,激活函数为relu,输出层1个节点,激活函数为sigmoid。

2.3 数据集简介

这里使用德国Rossmann超市2013、2014、2015三年的销售数据,具体数据文件包括:
train.csv-包括销售在内的历史数据
test.csv-测试数据(不包括销售)
store.csv-有关商店的补充信息
这些数据可从这里下载:https://www.kaggle.com/c/rossmann-store-sales/data
1、train.csv-包括销售在内的历史数据,共有9列,每列的含义如下:
date(日期):代表存储期
DayOfWeek(星期几):7表示周日,6表示周六以此类推
store(商店):每个商店的唯一ID
sale(销售):特定日期的营业额(这是您的预期)
customer(客户):特定日期的客户数量
open(开):为对存储是否被打开的指示:0 =关闭,1 =开
promo(促销):表示商店当天是否在进行促销
StateHoliday(州假日):通常,除州外,所有商店都在州法定假日关闭。请注意,所有学校在公共假日和周末都关闭。a =公共假期,b =复活节假期,c =圣诞节,0 =无
SchoolHoliday(学校假日):指示(商店,日期)是否受到公立学校关闭的影响
(1)导入数据

(2)查看前5行数据。

(3)查看是否有空值

Store 0
DayOfWeek 0
Sales 0
Customers 0
Open 0
Promo 0
StateHoliday 0
SchoolHoliday 0
dtype: int64
(4)查看各特征的不同值

共有几年的数据: [2015 2014 2013]
共有几种促销方法: [1 0]
2、store.csv数据集简介
StoreType- 区分4种不同的商店模型:a,b,c,d
Assortment分类 -描述分类级别:a =基本,b =额外,c =扩展
CompetitionDistance-距离最近的竞争对手商店的距离(以米为单位)
CompetitionOpenSince [Month / Year] -给出最近的竞争对手开放的大概年份和月份。
Promo促销 -表示商店当天是否在进行促销
Promo2 -Promo2是某些商店的连续和连续促销:0 =商店不参与,1 =商店正在参与
Promo2Since [年/周] -描述商店开始参与Promo2的年份和日历周。
PromoInterval-描述启动Promo2的连续间隔,并指定重新开始促销的月份。例如,“ Feb,May,Aug,Nov”表示该商店的每一轮始于该年任何一年的2月,5月,8月,11月

2.4使用Xgboost算法实例

2.4.1 读取数据

(1)导入模块

(2)定义数据预处理函数

(3)读取数据

2.4.2 预处理数据

(1)导入模块

(2)读取处理后的文件

(3)定义预处理store数据函数

(4)生成训练数据

2.4.3 保存预处理数据

(1)把各类别特征转换为整数,并保存为pickle文件

2.4.4 采样生成训练与测试数据

(1)读取数据

(2)生成训练与测试集

(3)定义采样函数

(4)采样数据

2.4.5 构建模型

(1)导入模块

(2)构建xgboost模型

2.4.6 训练模型

运行结果如下:
[2980] train-rmse:0.148366
[2981] train-rmse:0.148347
[2982] train-rmse:0.148314
[2983] train-rmse:0.148277
[2984] train-rmse:0.148238
[2985] train-rmse:0.148221
[2986] train-rmse:0.148218
[2987] train-rmse:0.148187
[2988] train-rmse:0.148182
[2989] train-rmse:0.148155
[2990] train-rmse:0.148113
[2991] train-rmse:0.148113
[2992] train-rmse:0.148067
[2993] train-rmse:0.148066
[2994] train-rmse:0.148064
[2995] train-rmse:0.148062
[2996] train-rmse:0.148048
[2997] train-rmse:0.148046
[2998] train-rmse:0.148046
[2999] train-rmse:0.148041
Result on validation data: 0.14628885960491078

2.5 使用NN算法实例

2.5.1 预处理数据

导入数据、对数据进行预处理,这些与2.4小节中的2.4.1、2.4.2一样,接下来对个特征先转换为数字,然后转换为one-hot编码,并保存。
(1)把数据转换为one-hot编码

(2)保存数据

2.5.2 生成训练数据

(1)读取数据

(2)生成训练数据

(3)定义采样函数

(4)通过采样生成训练数据

2.5.3 构建神经网络模型

(1)导入模块

(2)定义Model类

(3)构建神经网络

2.5.4 训练模型

运行结果:
Fitting NN...
Train on 200000 samples, validate on 84434 samples
Epoch 1/10
200000/200000 [==============================] - 12s 60us/sample - loss: 0.0121 - val_loss: 0.0142
Epoch 2/10
200000/200000 [==============================] - 11s 54us/sample - loss: 0.0080 - val_loss: 0.0104
Epoch 3/10
200000/200000 [==============================] - 11s 53us/sample - loss: 0.0071 - val_loss: 0.0100
Epoch 4/10
200000/200000 [==============================] - 11s 54us/sample - loss: 0.0064 - val_loss: 0.0099
Epoch 5/10
200000/200000 [==============================] - 11s 54us/sample - loss: 0.0059 - val_loss: 0.0098
Epoch 6/10
200000/200000 [==============================] - 11s 54us/sample - loss: 0.0055 - val_loss: 0.0103
Epoch 7/10
200000/200000 [==============================] - 11s 55us/sample - loss: 0.0051 - val_loss: 0.0100
Epoch 8/10
200000/200000 [==============================] - 11s 54us/sample - loss: 0.0047 - val_loss: 0.0099
Epoch 9/10
200000/200000 [==============================] - 11s 53us/sample - loss: 0.0045 - val_loss: 0.0102
Epoch 10/10
200000/200000 [==============================] - 11s 54us/sample - loss: 0.0042 - val_loss: 0.0100
Result on validation data: 0.10825838109273625

接下来将用更多实例,从多个角度比较NN与传统ML的比较,带EE(Entity Embedding)的NN与不带NN的比较,带EE的传统ML与不带EE的传统ML,以及EE在ML的顶级应用(如推荐算法、预测等问题上的使用)。
EE就像一座桥梁,把结构化数据与NN(或深度学习算法)连接在一起,大大发挥了NN在处理结构化数据方面的潜能(即NN强大的学习能力),深度学习将彻底改变传统机器学习。

机器学习或深度学习中如何更有效处理分类特征?

1 分类特征传统处理方法

分类(Categorical)特征常被称为离散特征、分类特征,数据类型通常是object类型,而机器学习模型通常只能处理数值数据,所以需要对Categorical数据转换成Numeric特征。
Categorical特征又有两类,我们需要理解它们的具体含义并进行对应的转换。
(1)有序(Ordinal)类型:
这种类型的Categorical存在着自然的顺序结构,如果你对Ordinal 类型数据进行排序的话,可以是增序或者降序,比如在衣服型号这个特征中具体的值可能有:S、M、L、XL等衣服尺码,其中S:(Small)表示小 ,M(Middle)表示中 ,L(Large)表示大,XL(extra large)表示加大尺码,它们之间有XL>L>M>S的大小关系。
(2)常规(Nominal)类型或无序类型:
这种是常规的Categorical类型,不能对Nominal类型数据进行排序,这类特征无谁大谁小之分。比如颜色特征可能的值有:red、yellow、blue、black等,我们不能说red>yellow>blue>black。
对于Ordinal和Nominal类型数据有不同的方法将它们转换成数字。

1.1 处理有序类型

对于Ordinal类型数据可以使用OrdinalEncoder、LabelEncoder进行编码处理,功能相同,都将每一个类别的特征转换成一个新的整数(0到类别数n-1之间)。例如S、M、L、XL等衣服尺码这四个类别进行OrdinalEncoder(或LabelEncoder)处理后会映射成0、1、2、3,这样数据间的自然大小关系也会保留下来。以下数据集data_set共有4列,这4列从左到右分别表示型号、颜色、性别、标签。其中型号为有序类别,其它都是常规类别。这些都是字符,现在利用sklearn中预处理模块preprocessing,把型号转换为数字,具体代码如下:
(1)导入数据

data_set的结果如下
array([['L', 'red', 'Female', 'yes'],
['M', 'red', 'Male', 'no'],
['M', 'yellow', 'Female', 'yes'],
['XL', 'blue', 'Male', 'no']], dtype='<U6')
(2)进行转换

1.2 处理常规类型

1.1节用OrdinalEncoder、OrdinalEncoder把分类变量型号转换为数字,同样可以把颜色、性别、标签等这些属于常规类型的特征转换为整数(0到类别数n-1之间)。具体代码如下:

1.2.1 把标签转换为数字

使用LabelEncoder把标签特征转换为数字。