前言
这篇文章写于2017年春,根据其他人的反映:本文用到的TensorFlow 1.x github的 textsum代码已经处于没有维护的状态,在FAQ中反映的错误也没有得到修复,因此我目前不会继续维护本文的代码部分。目前,textsum算法已经在各方面都劣于基于BERT的Transformer结构的算法(2018),无论你是学习、对比,我都不建议使用textsum。
此外这篇文章里面有错误,decode*** 是训练后产生的文件。 ref*** 是训练的标签,做参照用的(文章里面说错了)。感谢 知乎用户“晓阎”指出文章的错误。
这里不讨论算法的问题,这里讨论如何在Linux上安装并运行 这个开源项目,面向入门机器学习的读者,所以我会倾向于写得详细(啰嗦)。
关于textsum 的两篇文章是:
曾伊言:安装 TensorFlow 之 textsum 文章自动摘要zhuanlan.zhihu.com![3c547f8a8c769fcaa05ebe1a1c148ad1.png](https://i-blog.csdnimg.cn/blog_migrate/1a31d436e6622ee822fb5f3c94556219.jpeg)
关于如何安装运行 textsum 的文章,就是本文了↑
准备 textsum(文章自动摘要) 的数据集zhuanlan.zhihu.com另外一篇↑ 里面讲了如何制作自己的中文数据集,(从搜狗实验室下载数据集,并进行清洗)
介绍和资料
尽管这里只是说「安装和运行 textsum」,但是我们还是得简单知道一些算法知识,当然,时间不过可以跳过,直接ctrl + F 「安装」就能看到正文。
在GitHub的TensorFlow开源项目中,基于 LSTM(Long Short-Term Memory)模型的textsum,将输入的文章导入 RNN(Recurrent Neural Networks) 并输出得到的文章摘要。
![be0803099e26e5c05fcd77adf1f36a85.png](https://i-blog.csdnimg.cn/blog_migrate/ddb7742fd06c2c8ce30700d90caeb345.png)
![91f4cad1ebbde3cf918df5423403be5d.png](https://i-blog.csdnimg.cn/blog_migrate/3811b33ca595add172227715c49a0224.png)
![191539d04f48680d13266553ad8472a9.png](https://i-blog.csdnimg.cn/blog_migrate/8c0a148096f4e0d1ab27c2c72776404a.png)
图注:模型读取了输入句子 “ABC”,然后输出“WXYZ”。模型会在输出<结束标签>(<EOS>) 后停止预测内容。 注意! LSTM 是反向读取序列的,因为这样子做能建立起许多数据间短期的依赖关系,并让优化更加容易。(按照我的理解就是:越迟输入到RNN的text 它更容易被保留,而文章的开头一般会包含更多概括全文的信息)
Figure 1: Our model reads an input sentence “ABC” and produces “WXYZ” as the output sentence. The model stops making predictions after outputting the end-of-sentence token.
Note that the LSTM reads the input sentence in reverse, because doing so introduces many short term dependencies in the data that make the
optimization problem much easier.
简单的说,textsum 使用的 seq2seq model 就是:
用一个LSTM 去读取输入序列,每次用一个 timestep 去得到对输入序列的一个大的固定矢量描述,然后使用另一个LSTM 从这个矢量里提取出想要的输出序列。(见上图)
The idea is to use one LSTM to read the input sequence, one timestep at a time, to obtain large fixeddimensional vector representation, and then to use another LSTM to extract the output sequence from that vector (fig. 1).
By the way,
介绍算法的paper 在这里↓,自己去看
Sequence to Sequence Learning with Neural Networks.pdf
为何LSTM 要反向读取输入序列?Stack Overflow有较好的回答了↓
NStepLSTM and Seq2Seq model 然后 ctrl+F[reserved]
1.安装和运行 textsum
这里使用的是 TensorFlow + Bazel(需要前置 jdk 1.8)
环境 (报错很多时候与环境有关)
- Debian 8.9 Linux-3.16.0-4-amd64-x86_64-with-debian-8.9
- bazel 0.11.1 (构建文件系统)
- java 1.8.0_121
- Python 2.7.9 [GCC 4.9.2] on linux2
- Python 3.4.2 [GCC 4.9.1] on linux
- TensorFlow 1.6.0 (Python2)
- TensorFlow 1.6.0 (Python3)
安装Bazel,JDK 8,TensorFlow
『JDK 8 的安装』
Installing Bazel on Ubuntu
记得检查是否已经安装了 JDK 8,像我喜欢用 PyCharm写Python,早就装好了。
安装 jdk1.8 的一种方法
deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
『bazel 的安装』
sudo apt-get update && sudo apt-get install bazel
sudo apt-get upgrade bazel
bazelbuild/bazel←bazel下载地址,打开之后找匹配的版本
![e5be468b97761031102d2868e4e31dca.png](https://i-blog.csdnimg.cn/blog_migrate/15f1aaea228aa05079322c6344f71fb9.jpeg)
『TensorFlow 的安装』
TensorFlow 推荐直接用Python 的pip 安装。pip版本推荐大于 9.0.1
pip install tensorflow
注意,我运行的时候,没有指定运行运行时候的Python 版本,使得我的系统默认调用 Python2来运行这个程序。所以如果你安装了 TensorFlow 而报错的时候显示没有找到,那么你可能要看看另外一个版本的Python 是否也安装了TensorFLow。
『textsum 代码文件夹的下载』
下载GitHub上 TensorFlow 开源项目的 textsum 模型,就是直接把整个文件夹复制下来
tensorflow/modelsgithub.com![91f4cad1ebbde3cf918df5423403be5d.png](https://i-blog.csdnimg.cn/blog_migrate/3811b33ca595add172227715c49a0224.png)
由于GitHub没有提供下载某个文件夹的按钮,似乎想要获得textsum 就需要下载整个TensorFlow,显然,这是愚蠢的行为,我们不应该去做。
比较Geek的处理方法是:登录某些网站(DownGit),粘贴连接,直接下载。
https://minhaskamal.github.io/DownGit/minhaskamal.github.ioDownGit
DownGitminhaskamal.github.io![b6a417d37af682931622195a4339db6d.png](https://i-blog.csdnimg.cn/blog_migrate/c661d680aa3a18c542b1042c9d37bff4.jpeg)
『检查』
安装完 jdk 8,bazel,TensorFLow后,记得看看是否装上了,我喜欢顺便看看版本
java -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
bazel version
Build label: 0.11.1
Build target: bazel-out/k8-opt/bin/src/main/java/com/google/devtools/build/lib/bazel/BazelServer_deploy.jar
Build time: Tue May 14 07:48:23 +50148 (1520362424903)
Build timestamp: 1520362424903
Build timestamp as int: 1520362424903
>>> import tensorflow as tf
>>> tf.__version__
'1.6.0'
2.准备工作目录
参考textsum 目录(就是上面通过GitDown下载的textsum.zip)中的 README.md 文件,安装这里面提供的步骤,构建工作目录。
当然这一份 README.md ←你已经见过了,就在 GitHub textsum 的那个页面下方。
首先找一个合适的地方,建立一个不被打扰的工作目录,比如我
cd Videos # 刚好在资源管理器有按钮可以直接去
mkdir Yonv_CWD # Current Working Directory,Yonv这个名字是我随便起的
cd Yonv_CWD
#!注意,这里新建一个空的文件(注意不是文件夹而是空文件)
# WORKSPACE 是 bazel生成工作环境所必须的一个标记
touch WORKSPACW
# 这里是放训练集的,里面待会需要放入 二进制文件training-* 和 文本文件vocab
mkdir data
# 然后,退回到原来的 Yonv_CWD,开始用 bazel 建立工作环境
cd ..
bazel build -c opt --config=cuda textsum/...
# 注意,如果你的电脑没有 CUDA (Compute Unified Device Architecture)
# 也就是没有NVIDIA 的GPU的话,这一项就没必要加了
# 注意,textsum/... 后面的三个“...”
$ bazel build -c opt textsum/...
...............
INFO: Analysed 7 targets (12 packages loaded).
INFO: Found 7 targets...
INFO: Elapsed time: 2.153s, Critical Path: 0.02s
INFO: Build completed successfully, 1 total action
# 强行加上也没关系,反正它只是WARRNING
# 再者说,学C++ 的人,难道会在乎终端返回的 WARRNING?
$ bazel build -c opt --config=cuda textsum/...
...............
WARNING: Config values are not defined in any .rc file: cuda
INFO: Analysed 7 targets (12 packages loaded).
INFO: Found 7 targets...
INFO: Elapsed time: 3.325s, Critical Path: 0.04s
INFO: Build completed successfully, 4 total actions
![165d2888132c16e52db60cfa7aec38fd.png](https://i-blog.csdnimg.cn/blog_migrate/c5eba7d800b919011b575e1b46d2ab17.jpeg)
3.运行textsum
3.1 准备数据集
在GitHub下载的 textsum中,有用于测试的数据集,作者把这个数据集称为 toy,这个玩具一样的测试数据集很小,只有33KB,姑且将就一下。
把 YonvCWD/textsum/data 里面的 data 和 vocab 文件复制(反正不大)到 Yonv_CWD/data 里面,然后把 data文件重命名为 training-0(非必要)
cd Yonv_CWD
cp testsum/data/vocab data/vocab
cp testsum/data/data data/training-0
3.1 生成自己的数据
关于textsum 中文训练集的生成,可以看我的另外一篇:
值得一提的是:为了加速计算,数据集除了 vocab,像 training-* validation-* test-* 都是二进制的,可以用 textsum/data/convertexample.py 去转化,转化的代码记录在 这个py文件的开头备注里面了,摘抄出来是↓
python data_convert_example.py
--command binary_to_text
--in_file data/data
--out_file data/text_data
python data_convert_example.py
--command text_to_binary
--in_file data/text_data
--out_file data/binary_data
![c2a826bc98c218698d74383ef193e0f9.png](https://i-blog.csdnimg.cn/blog_migrate/30df80f97dd443521334478ad58e46ee.jpeg)
左上:Yonv_CWD/data文件夹中的数据集
左下:Yonv_CWD/textsum/data文件夹中,尝试用convertexample.py 程序进行二进制 和 文本之间的转化: data(二进制) → testdata(文本)→ binary_data(二进制)
右二:打开后的vocab文件,可以看到是单词的频数表,其中<UNK></d>等是标签
右一:转化后的test_data文件,可以看到有 "abstract=" , "article=" 的标记
虽然这里使用了textsum 提供的测试数据(toy ^_^),少了数据获取和清洗步骤,但是我们总算是可以继续进行下去了。
3.2 训练、评估、输出
依照README.md 提供的语句,我们有三段脚本需要运行,依次是训练、评估、输出
# Run the training.
$ bazel-bin/textsum/seq2seq_attention
--mode=train
--article_key=article # 对应training-0(更名前是data文件)中的 标签"article"
--abstract_key=abstract # 对应training-0(更名前是data文件)中的 标签"abstract"
--data_path=data/training-* # 这个文件是之前准备的
--vocab_path=data/vocab
--log_root=textsum/log_root # 中间文件会保留在这里
--train_dir=textsum/log_root/train
# Run the eval. Try to avoid running on the same machine as training.
$ bazel-bin/textsum/seq2seq_attention
--mode=eval
--article_key=article
--abstract_key=abstract
--data_path=data/validation-* # 如果你只是想测试依然程序是否正常,直接复制 training-0吧
--vocab_path=data/vocab
--log_root=textsum/log_root
--eval_dir=textsum/log_root/eval
# Run the decode. Run it when the model is mostly converged.
$ bazel-bin/textsum/seq2seq_attention
--mode=decode
--article_key=article
--abstract_key=abstract
--data_path=data/test-* # 如果你只是想测试依然程序是否正常,直接复制 training-0然后改名吧
--vocab_path=data/vocab
--log_root=textsum/log_root
--decode_dir=textsum/log_root/decode
--beam_size=8
如果运行成功,那么它们的结果依次是:
training部分
$ bazel-bin/textsum/seq2seq_attention
--mode=train
--article_key=article
--abstract_key=abstract
--data_path=data/training-*
--vocab_path=data/vocab
--log_root=textsum/log_root
--train_dir=textsum/log_root/train
WARNING:tensorflow:From /usr/local/lib/python2.7/dist-packages/tensorflow/python/ops/nn_impl.py:1346: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Future major versions of TensorFlow will allow gradients to flow
into the labels input on backprop by default.
See tf.nn.softmax_cross_entropy_with_logits_v2.
WARNING:tensorflow:From /home/user/Videos/CWD/bazel-bin/textsum/seq2seq_attention.runfiles/__main__/textsum/seq2seq_attention.py:96: __init__ (from tensorflow.python.training.supervisor) is deprecated and will be removed in a future version.
Instructions for updating:
Please switch to tf.train.MonitoredTrainingSession
2018-03-19 13:55:43.347757: I tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
running_avg_loss: 9.001080
running_avg_loss: 8.945733
...
吃个饭回来
...
running_avg_loss: 0.000241
running_avg_loss: 0.000298
eval 部分(validation)
$ bazel-bin/textsum/seq2seq_attention
--mode=eval
--article_key=article
--abstract_key=abstract
--data_path=data/validation-*
--vocab_path=data/vocab
--log_root=textsum/log_root
--eval_dir=textsum/log_root/eval
running_avg_loss: 0.000611
running_avg_loss: 0.000951
running_avg_loss: 0.000459
running_avg_loss: 0.000200
...
prediction 部分(test)输出部分
输出结果会在
Yonv_CWD/textsum/log_root/decode 文件夹中看到
user@debian:~/Videos/Yonv_CWD/textsum/log_root/decode$ zsh
➜ decode ls
decode1521440441 ref1521445096
➜ decode ls -al
总用量 1360
drwxr-xr-x 2 user user 4096 3月 19 22:31 .
drwxr-xr-x 5 user user 4096 3月 19 17:50 ..
-rw-r--r-- 1 user user 0 3月 19 14:20 decode1521440441
-rw-r--r-- 1 user user 647350 3月 19 17:50 decode1521445096
-rw-r--r-- 1 user user 0 3月 19 14:20 ref1521440441
-rw-r--r-- 1 user user 729342 3月 19 17:50 ref1521445096
可以看到,如果运行失败,那么即便会产出文件,它也是空的
成功产出的文件打开后是这样的
![5e89a25925257e01e32db7569262d869.png](https://i-blog.csdnimg.cn/blog_migrate/3a993d14fd13b7a9e0b3fa8b776c8c3e.jpeg)
output=czechs outdrink germans in beer-guzzling stakes .
output=mexican financial markets end turbulent week .
output=abb to sell electrical explosion-proof equipment business .
output=mexican financial markets end turbulent week .
output=moslem rights group wants narcotic khat legalised .
output=abb to sell electrical explosion-proof equipment business .
output=mexican financial markets end turbulent week .
output=croatians vote in three constituencies .
如果你没有成功输出,那么可以看看我下面的『错误处理』
3.3错误处理
运行中如果出现类似错误,那么是因为你vocab格式不对,里面本来会有<标签>的,你需要17832:
Bad line:
...
assert vocab.CheckVocab(data.SENTENCE_START) > 0
运行中如果出现类似错误,那么是因为你数据集 training-0 格式不对,你需要17832:
Exception in thread Thread-24809:
...
DecodeError: Error parsing message
...
MemoryError
运行中如果出现类似错误,不要理会它,没事的,相反的,如果你改了state_is_tuple=False,那么当TensorFlow 版本大于1.0 的时候你反而会出错:
...
state_is_tuple=False
...
运行中如果报错,类似于(出现某个很大的多维数组):
ValueError: Could not flatten dictionary. Key had 2 elements, but value had 1 elements. Key: [<tf.Tensor 'seq2seq/encoder3/bidirectional_rnn/fw/fw/cond_119/Merge_1:0' shape=(8, 256) dtype=float32>, <tf.Tensor 'seq2seq/encoder3/bidirectional_rnn/fw/fw/cond_119/Merge_2:0' shape=(8, 256) dtype=float32>], value: [array([[[ 2.723167 , -1.924321 , -0.09930453, ..., -0.57662404,
-1.633333 , 5.6171 ],
[ 2.723167 , -1.924321 , -0.09930453, ..., -0.57662404,
-1.633333 , 5.6171 ],
[ 2.723167 , -1.924321 , -0.09930453, ..., -0.57662404,
-1.633333 , 5.6171 ],
...,
...,
[ 2.723167 , -1.924321 , -0.09930453, ..., -0.57662404,
-1.633333 , 5.6171 ],
[ 2.723167 , -1.924321 , -0.09930453, ..., -0.57662404,
-1.633333 , 5.6171 ],
[ 2.723167 , -1.924321 , -0.09930453, ..., -0.57662404,
-1.633333 , 5.6171 ]]], dtype=float32)].
那么是因为TensorFlow 1.0>= 的兼容问题
只要把 seq2seq_attention_model.py 文件中的 [state_is_tuple=True] 改回 [state_is_tuple=False]] (然而好多人需要把这个改回来,是因为想解决之前的另一个报错,才修改了这个文件(又改回来了))
yONV_CWD/textsum/seq2seq_attention_model.py
# replace [state_is_tuple=False] as [state_is_tuple=True]
他们↓ 也遇到这个问题了,并且ctrl+f[fix] 在某一行可以找到解决方法,就是把 文件中的[state_is_tuple=True] 改回 [state_is_tuple=False]
#TextSum# - Error running decode: Could not flatten dictionary. Key had 2 elements, but value had 1 elements · Issue #417 · tensorflow/models