开始撸代码:
1.先定义参数,
batch_size:64
epoch:40
hidden_dim:300
learning_rate:0.001
dropout:0.5
embedding_dim:300
optimizer:Adam
clip:5.0(梯度截断,防止梯度爆炸和消失)
2.是用预训练好的字向量还是随机初始化
随机的话:np.float(np.random.uniform(-0.25, 0.25, (len(vocab), embedding_dim)))
预训练:np.array(np.load(embedding_path), dtype=’float32’) 格式大概就是一个字对应一个300维的向量
3.读取train_data和test_data
读完之后的格式是一个数组,每个元素包含的是每个段落的所有元素和标签
4.数据基本整理完了,那就开始模型的构造了,也就是单层的BiLSTM+CRF层,学完了上面的理论知识,太好理解了
- def__init__():相关参数赋值
- 相关palceholder:
self.word_ids = tf.olaceholder(tf.int32, shape=[None, None], name=”word_ids”)
self.labels = tf.placeholder(tf.int32, shape=[None, None], name=”labels”)
self.sequence_lengths = tf.olaceholder(tf.int32, shape=[None], name=”sequence_lengths”)
self.dropout_pl = tf.placeholder(tf.float32, shape=[], name=”dropout_pl”)
self.learning_rate = tf.placeholder(tf.float32, shape=[], name=”learning_rate”)
3. Embedding层:定义变量形状、初始化及输出相应字坐标的字向量,dropout是防止过拟合,随机不更新部分参数。 word_ids的shape应该是batch_size * sequence_lengths
with tf.variable_scope(“words”):
_word_embedding = tf.Variable(self.embeddings,dtype=tf.float32,
trainable=True,
name=”_word_embedding”)
word_embedding=tf.nn.embedding_lookup(params=_word_embedding,
ids= self.word_ids,
name=”word_embedding”
self.word_embeddings = tf.nn.dropout(word_embedding, self.dropout_pl)
4. Bilstm层
with tf.variable_scope(“bi-lstm”):
cell_fw = LSTMCell(self.hidden_dim)
cell_bw = LSTMCell(self.hidden_dim)
(output_fw_seq, output_bw_seq), _ = tf.nn.bidirectional_dynamic_rnn(
cell_fw=cell_fw, cell_bw=cell_bw, inputs=self.word_imbeddings,
sequence_length=self.qequence_lengths, dtype=tf.float32)
output = tf.concat([output_fw_seq, output_bw_seq], axis=-1)
output = tf.nn.dropout(output, self.dropout_pl)
权重层
with tf.variable_scope(“W”)
W = tf.get_variable(name=”W”, shape=[2 * self.hidden_dim, self.num_tags],
Inittializer=tf.contrib.layer.xavier_initializer(), dtype=tf.float32)
b = tf.get_variable(name=”b”, shape=[self.num_tags],
Inittializer=tf.contrib.layer.xavier_initializer(), dtype=tf.float32)
s = tf.shape(output) 我的理解这里矩阵应该是batch_size * sequence_lengths * (2 * self.hidden_dim)
output = tf.reshape(output, [-1, 2 * self.hidden_dim]) (batch_size* sequence_lengths) * (2 * self.hidden_dim)
pred = tf.matmul(output, W) + b (batch_size* sequence_lengths) * num_tags
self.logits = tf.reshape(pred, [-1, s[1], self.num_tags]) a[1]是sequence_lengths
batch_size* sequence_lengths* num_tags
5. loss层
log_likelihood, self.transition_params = crf_log_likelihood(inputs=self.logits,
tag_indices=self.labels, batch_size* sequence_lengths(真实标签)
sequence_lengths=self.sequence_lengths)
我必须得细讲一下crf_log_likelihood这个函数,百度别人的都是把注释翻译一遍,我现在需要了解的是里面用到了什么函 数,返回的具体是什么:
首先输入:inputs->batch_size * sequence_lengths * num_tags,每个元素对应每个标签的概率
tag_indices-> batch_size * sequence_lengths ,每个元素对应的真是标签
sequence_lengths->每个batch_size的长度
transition_params->一个[num_tags, num_tags]矩阵
输出呢:log_likelihood->英文是说a [batch_size] tensor containing the log_likelihood of each
example, given the sequence of tag indices,应该是batch_size * 1,即
每个序列的得分,也就是真实路径的那个得分吧,在理论知识里面应该
就是那个可能性,下面求loss的时候加了负号,大概就是想要求loss
最小。这个里面应该包含了动态规划算法和viterbi算法)
transition_params->一个[num_tags, num_tags]矩阵
过程:1. 如果transition_params是None,则初始化一个
2. crf_sequence_score(),函数返回的是未归一化的得分,batch_size * 1
3. crf_log_norm(),函数返回的是归一化的得分,batch_size * 1
4. 两者相减肯定是batch_size * 1,这就是这个函数的结果,我有点迷惑
self.loss = -tf.reduce_mean(log_likelihood)
tf.summary.scalar(“loss”, self.loss)
6. train层:
with tf.variable_scope(“train”):
self.global_step = tf.Variable(0, name=”global_step”, trainable=False)
optim = tf.train.AdamOptimizer(learning_rate=self.learning_rate)
grads_and_vars = optim.compute_graduents(self.loss)
grads_and_vars_clip = [ [ tf.clip_by_value(g, -self.clip_grad, self.clip_grad), v] for g, v in grads_and_vars]
对梯度值进行截断,防止出现梯度消失和爆炸
self.train_op = optim.apply_gradients(grads_and_vars_clip, global_step=self.global_step)
self.init_op = tf.global_variables_initializer()
5. 模型架构写完之后就是加入数据集了
- 对训练集根据batch_size的大小随机划分
- 进行padding,长的截取,短的补0
- 对feed_dict的元素进行赋值
- 然后就是sess.run( [self.train_op, self.loss, self.merged, self.global_step], feed_dict=feed_dict)
- 这里没有评估集,直接是测试集,也可以加上评估集
预测一个batch的结果时,
logits, transition_params = sess.run([self.logits, self.transition_params], feed_dict=feed_dict)
logits里面保存到的是64*300*7,每个元素对应的label的概率,然后针对 每个sequence利用veterbi_decode函数可以直接 得到最优路径,再与真实坐标多对比就可以获得test的loss和准确率(貌似也可以直接获取test的损失,实际上就相当于预 测了)