word2vec + manage experiments
关键词:model base, variable sharing, model sharing
word2vec
- 文本的分布式表示是许多自然语言处理任务的基础
- word2vec是用来生成词语分布式表示的一组模型
- 主要有两种模型:skip-gram和CBOW
- 算法角度:CBOW模型从上下文词预测目标词,skip-gram模型从目标词预测上下文词
- 统计角度:CBOW模型将an entire context as one observation,这样做smoothes over了许多分布信息,适合小一些的数据集上;skip-gram模型则是将each context-target pair as a new observation,在larger datasets上表现更好
- 训练词向量:定义一个单层网络,任务是给定中心词预测词典中的词作为上下文词的概率,我们最后要的是隐含层的权重参数
- 使用 softmax 来获得可能的目标词的分布,分母要对字典中的所有词取指数再求和,计算是瓶颈
- 规避瓶颈的方法:层次化的softmax 和基于采样的 softmax
- 文章**Distributed Representations of Words and Phrases and their Compositionality **指出,训练skip-gram模型时,与更复杂的分层softmax相比,负采样可以加快训练速度,为频繁词汇提供更好的向量表示
- 负采样实际上是一种称为噪声对比估计(NCE)的简化模型,基于假设,如噪声样本的数量k和噪声样本的分布Q满足kQ(w) = 1,来简化计算;理论上不能保证其导数和softmax梯度一致
- NCE则随着noise样本增多,提供了这种保证
- 负采样和NCE只在训练时有用
Implementing
- 词的indices作为输入(一个scalar),
- BATCH_SIZE的样本,输入维度为[BATCH_SIZE],输出维度为[BATCH_SIZE,1]
- 词向量矩阵维度为[VOCAB_SIZE,EMBED_SIZE],每一行代表一个词向量
- 利用tf.nn.embedding_lookup()找中心词对应的向量,免去了不必要的计算(matrix and onehot vector)
- loss使用tf.nn.nce_loss(),optimizer使用GradientDescentOptimizer
Structure TF models
定义图
- 导入数据(placeholder or tf.data)
- 定义权重
- 定义模型
- 定义损失函数
- 定义优化器
执行图
- 初始化所有变量
- 初始化迭代器或者feed in训练数据
- 数据经过模型得到结果
- 计算cost
- 调整模型参数使得cost最小或者最大
build model as a class in order to reuse easily.
Variable sharing
Name scope
将相关的ops放在一个name_scope下,这样得到的图在TensorBoard上是一块一块的,更加整洁。
TensorBoard图中三种边:
- 灰实边:数据流
- 橙实边:参考边,op_lest影响op_right
- 灰虚边:控制依赖边,op_left依赖于op_right
Variable scope
和Name scope一样都创建了namespace,调用tf.variable_scope(“name”)会隐式地调用tf.name_scope(“name”),Variable scope主要功能是促进变量共享(facilitate variable sharing)
为实现变量共享:
- 使用 tf.get_variable(),它会在创建变量之前检查其是否存在
- 将所用到的变量放到一个VarScope,将这个VarScope设置为可复用的(reusable)
1 | def fully_connected(x,output_dim,scope_name): # 基础组件:全连接层 |
以上代码模式,基础组件可以定义更多,比如conv,relu,网络结构可以更复杂,比如放一个ResNet,非常容易scale。
由于使用了变量共享,多次传入x,网络TensorBoard图的复杂程度不会爆炸式增加。
Graph collections
使用这个,可以获取满足一定条件的所有变量,tf.get_collection(key,scope=None)
比如执行optimizer的时候,默认情况下它会获取key=tf.GraphKeys.TRAINABLE_VARIABLES的变量,即所有可训练的变量(当然也可以传入指定的、要训练的变量
获取某个scope下的所有变量,tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,scope=’scope_name’)
key还有许多其它的值,见官网。
Manage experiments
实验总是很久,中断随时发生,因此训练可以随时随地停止、像没事一样恢复,非常重要。
另外一个问题是论文结果复现,控制实验的随机因子对复现结果非常关键。
tf.train.Saver()
周期性地保存模型参数是个好习惯
tf.train.Saver()类将图的变量保存(不是整张图)到二进制文件,也就是一个checkpoint(变量名到tensors的映射)
1 | #定义模型 |
生成的checkpoint名,像这样,’checkpoints/skip-gram-10000’
在恢复模型时可以直接传入checkpoint名(如果有的话
1 | ckpt = tf.train.get_checkpoint_state(os.path.dirname('checkpoints/model-name')) |
恢复的时候,网络图还得自己重新搭(still have to create the graph ourselves)之后再加载变量
当然,经常的做法是到目前为止表现最好的参数也保存下来(不止是最近的一次)
tf.summary
记录模型训练过程中指标变化,包括loss,accuracy等等
1 | #创建summaries |
control randomization
为了使得别人在实验时结果会与你一致
op级别
所有的tensor初始化时都传入seed参数
session记录了随机状态,每一个新的session都会重新start the random state1
2
3
4
5
6c = tf.random_uniform([],-10,10,seed=2)
d = tf.random_uniform([],-10,10,seed=2)
with tf.Session() as sess:
print(sess.run(c)) #value: a
print(sess.run(d)) #same value: a
print(sess.run(c)) #value: bgraph级别
比如demo1.py和demo2.py代码相同,设置了tf.set_random_seed(seed)的话执行结果是相同的1
2
3
4
5
6tf.set_random_seed(2)
c = tf.random_uniform([],-10,10)
d = tf.random_uniform([],-10,10)
with tf.Session() as sess:
print(sess.run(c)) # 都是a
print(sess.run(d)) # 都是b