==================================================================
Get-to-the-point: Summarization with pointer-generator networks
==================================================================
Attr
=======
author
--------
See Abigail
Google Brain & stanford NLP
Run code
---------
repo: https://github.com/abisee/pointer-generator
1. download data
作者给出了完整的数据处理流程,放在 https://github.com/abisee/cnn-dailymail 下。
不过我们使用一个用户(见讨论 https://github.com/abisee/cnn-dailymail/issues/9 )提供的已经处理完的数据,结果在 https://github.com/JafferWilson/Process-Data-of-CNN-DailyMail
下载其中的 *FINISHED_FILES* 对应的链接里的文件,得到一个zip,解压后是如下的结构:
::
chunked/
test_000.bin
...
test_011.bin
train_000.bin
...
train_287.bin
val_000.bin
...
val_013.bin
test.bin
train.bin
val.bin
vocab
其中 ``chunked`` 存储的是将外边对应的文件按照1K每份做划分后的结果——划分是为了后面训练时多进程读取输入。
*\*.bin* 是使用 ``abisee/cnn-dailymail`` 仓库中的 ``make_datafiles.py`` 脚本处理原生txt的结果。
处理过程大概是用 ``tensorflow.core.example.example_pb2.Example``
对象来存储tokenize过的article和abstract,
然后序列化这个对象,
并以二进制方式写入到文件。则应该是tf标准的处理方式。暂时先不管。
2. 安装tf
还是觉得在公司电脑上运行,所以登上去用virtualenv 装了tf1.2;但是看README是1.2.1,不知道有没有差别;
此外,需要重新把文件下载到服务器上。好麻烦……
3. 运行
搞定。用tf1.2顺利把train跑起来了。还通过tensorbord看了下loss(scalar);不过看graph和embedding都失败了。
应该是没有把graph保存下来;embedding查看则一直在parsing,不知道是不是工作用的Mac air性能太差了。
不管怎么说,前期运行已经OK了。下面就是看代码了!
Reading Code
-------------
主入口是 ``run_summarization.py``
++++++++++++++++++++++++++++++++++
::
part1: config-argument definitions
part2: global function
a. calc_running_avg_loss
平滑loss;每一步将之前的累积loss decay,同时加上缩放过的当前一步的loss,得到新的累计loss
以此新的累计loss为当前的loss
b. restore_best_model
将eval的模型拷贝到train下;其中用到了tf的参数恢复的能力!
c. convert_to_coverage_model
转换模型
d. setup_training
设置training & 跑training; 使用了tf.train.Supervisor
e. run_training
f. run_eval
g. main
判定是解码时,设置 batch-size = beam-size. 利用是
in decode mode, we decode one example at a time. On each step, we have
beam_size-many hypotheses in the beam,
so we need to make a batch of these hypotheses.
应该得结合beam-search怎么做的来看了。
用一个namedtuple来存储flags的解析的且需要的东西
用 tf.set_random_seed 来设置随机数种子
在decode模式下可以看到:将 max_dec_steps 强制设为1了;解释说是每次做一步!然后调用的是
BeamSearchDecoder.decode 来做的。
接下来看下 ``model.py``
+++++++++++++++++++++++++++++++
这个应该是我们核心要学习的。
::
SummarizationModel
_add_placeholders
输入部分
enc_batch, int32, (batch-size, None), 说明每次每个batch的size部署固定的!
enc_len, int32, (batch-size), batch中每个句子的长度?
enc_padding_mask, float32, (batch-size, None) mask 处理!
如果是 pointer-gen 网络
enc_batch_extend_vocab, int32, (batch-size, None) 每个instance的扩展vocab
max_art_oovs, int32, [], 不清楚是什么
dec_batch, int32, (batch-size, None)
target_batch, int32, (batch-size, None)
dec_padding_mask, float32, (batch-size, None)
decode & coverage时
prev_coverage, float32, (batch-size, None)
_add_encoder
创建lstm cell,用的是
tf.contrib.rnn.LSTMCell
这个和 tf.nn.rnn_cell.LSTMCell 是等价的(alais); initializer 用的是同一个initializer:
self.rand_unif_init = tf.random_uniform_initializer(
- rand_unif_init_mag, rand_unif_init_mag, seed=123
)
state_is_tuple = True, 默认行为;返回的结果有
创建双向LSTM网络,用的是
tf.nn.bidirectional_dynamic_rnn
传入了 sequence_len,这个应该是来自data部分的输入;
swap_memory = True, 这个参数的解释是
Transparently swap the tensors produced in forward inference but needed for back prop
from GPU to CPU. This allows training RNNs which would typically
not fit on a single GPU, with very minimal (or no) performance penalty.
似乎是多GPU时把这个参数打开;默认是False的;
``data.py`` 模块处理数据
++++++++++++++++++++++++++++++++
比较有意思的:
在从外部字典文件加载到内部字典时,会把最后一个加入的词打出来——方便定位,很细心。
如下是特殊字符:
::
# and are used in the data files to segment the abstracts into sentences.
# They don't receive vocab ids.
SENTENCE_START = ''
SENTENCE_END = ''
PAD_TOKEN = '[PAD]' # This has a vocab id, which is used to pad the encoder input, decoder input and target sequence
UNKNOWN_TOKEN = '[UNK]' # This has a vocab id, which is used to represent out-of-vocabulary words
START_DECODING = '[START]' # This has a vocab id, which is used at the start of every decoder input sequence
STOP_DECODING = '[STOP]' # This has a vocab id, which is used at the end of untruncated target sequences
包含一个用于TensorBoard可视化的函数。
有一个函数来生成 ``tf.Example`` , 其中用 ``glob.glob`` 函数来扩展通配符,这个挺不错的。从data中读取tf.Example
时,用了 `struct` 这个标准库,这个是用来以二进制方式在C类型与Python类型做交换的;不是特别懂这个,可能算是序列化的
一种方式?用 ``struct.unpack`` 来完成的。
原来,虽然在generator机制下,input中的UNK(不出现在全局词典中)会被拿出来作为额外的字典;
但是训练语料中abstract的内容中,可能仍然含有不在全局词典 + input额外词典中的词,所以 **训练语料的输入中,还是有可能有UNK!**