開始撸代碼:
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的損失,實際上就相當于預 測了)