技术标签: tensorflow AI练手系列 nlp 深度学习 自然语言处理 神经网络
项目是百度AI studio比赛,题目为“汽车大师问答摘要与推理”。
要求大家使用汽车大师提供的11万条(技师与用户的多轮对话与诊断建议报告数据)建立模型,模型需基于对话文本、用户问题、车型与车系,输出包含摘要与推断的报告文本,综合考验模型的归纳总结与推断能力。该解决方案可以节省大量人工时间,提高用户获取回答和解决方案的效率。
对于每个用户问题"QID",有对应文本形式的文本集合 D = “Brand”, “Collection”, “Problem”, “Conversation”,要求阅读理解系统自动对D进行分析,输出相应的报告文本"Report",其中包含摘要与推理。目标是"Report"可以正确、完整、简洁、清晰、连贯地对D中的信息作归纳总结与推理。
训练:所提供的训练集(82943条记录)建立模型,基于汽车品牌、车系、问题内容与问答对话的文本,输出建议报告文本
输出结果:对所提供的测试集(20000条记录)使用训练好的模型,输出建议报告的结果文件,通过最终测评得到评价分数。
import pandas as pd
train_path = "E:/课程/07 名企班必修课1-导师制名企实训班自然语言处理方向 004期/Lesson1-项目导论与中文词向量实践/数据集/AutoMaster_TrainSet.csv"
test_path = "E:/课程/07 名企班必修课1-导师制名企实训班自然语言处理方向 004期/Lesson1-项目导论与中文词向量实践/数据集/AutoMaster_TrainSet.csv"
df = pd.read_csv(train_path, encoding='utf-8')
df.head()
原始数据展示:
超参数定义->设置使用GPU->训练->验证->测试->发现问题->改进问题
代码详见/bin/main.py
关键参数简要说明:
max_enc_len = 200 #encoder输入最大长度
max_dec_len = 40 #decoder输入最大长度
batch_size = 32
beam_size = 3
vocab_size = 30000
embed_size = 256
enc_units = 256
dec_units = 256
attn_units = 256
learning_rate = 0.001
steps_per_epoch = 1300
max_steps = 10000
epochs = 20
dropout_rate = 0.1
#设置使用GPU还是CPU
gpus = tf.config.experimental.list_physical_devices(device_type='GPU')
#若使用GPU,对使用哪一块GPU进行设置
if gpus:
tf.config.experimental.set_visible_devices(devices=gpus[0],device_type='GPU')
数据读取与构建词向量,代码详见preprocess.py、data_reader.py、build_w2v.py。
数据分批与数据规范化
定义Encoder与Decoder模块,及中间的Attention部分。
Encoder部分:
embedding维度256维
self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim, weights=[embedding_matrix], trainable=False)
网络采用双向GRU作为编码器(由两个单向的GRU拼接而来)。
self.gru = tf.keras.layers.GRU(self.enc_units, return_sequences=True, return_state=True, recurrent_initializer='glorot_uniform') self.bigru = tf.keras.layers.Bidirectional(self.gru, merge_mode='concat')
同时隐藏态也需要拼接:
#把隐藏层划分成几个子张量,分成2份,在第二个维度上进行切分 hidden = tf.split(hidden, num_or_size_splits=2, axis=1) output, forward_state, backward_state = self.bigru(x, initial_state=hidden) state = tf.concat([forward_state, backward_state], axis=1)
详细代码见rnn_encoder.py。
Attention部分:
Score计算采用感知机的方式,再将Score经过一个Softmax归一化得到Attention_Weight。
# Calculate v^T tanh(W_h h_i + W_s s_t + b_attn) score = self.V(tf.nn.tanh(self.W1(enc_output) + self.W2(hidden_with_time_axis))) attn_dist = tf.nn.softmax(score,axis=1)#shape= (16,200,1)
然后reduce_sum得到context_vector
context_vector = tf.reduce_sum(attn_dist * enc_output, axis=1)
详细代码见rnn_decoder.py。
Decoder部分:
embedding维度256维:
#定义Embedding层,加载预训练的词向量 self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim, weights=[embedding_matrix], trainable=False)
网络采用单向GRU作为编码器:
self.gru = tf.keras.layers.GRU(self.dec_units, return_sequences=True, return_state=True, recurrent_initializer='glorot_uniform')
GRU的输入是由输入x和context_vector组成:
x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
GRU输出后需要维度变化然后再过一个FC层输出概率分布:
output, state = self.gru(x) output = tf.reshape(output, (-1, output.shape[2]))#(batch_size,hidden_size) out = self.fc(output)#(batch_size,hidden_size)
代码在rnn_encoder.py中。
整体模型定义上还会有一些细节,例如Tearcher的使用等等,整体模型定义代码如下:
import tensorflow as tf
from seq2seq_tf2.encoders import rnn_encoder
from seq2seq_tf2.decoders import rnn_decoder
from utils.data_utils import load_word2vec
import time
class SequenceToSequence(tf.keras.Model):
def __init__(self, params):
super(SequenceToSequence, self).__init__()
#读取word2vec.txt、vocab.txt返回词向量矩阵,矩阵大小vacab_size*embed_size,每一行都是行索引编号对应word的词向量
self.embedding_matrix = load_word2vec(params)
self.params = params
self.encoder = rnn_encoder.Encoder(params["vocab_size"],
params["embed_size"],
params["enc_units"],
params["batch_size"],
self.embedding_matrix)
self.attention = rnn_decoder.BahdanauAttention(params["attn_units"])
self.decoder = rnn_decoder.Decoder(params["vocab_size"],
params["embed_size"],
params["dec_units"],
params["batch_size"],
self.embedding_matrix)
def call_encoder(self, enc_inp):
enc_hidden = self.encoder.initialize_hidden_state()#[batch_size,enc_units]
enc_output, enc_hidden = self.encoder(enc_inp, enc_hidden)
return enc_output, enc_hidden
def call(self, enc_output, dec_inp, dec_hidden, dec_tar):
predictions = []
attentions = []
#初始化Attention,主要是初始化隐藏层和内部各个参数,隐藏层维度先初始化与encoder一致
context_vector, _ = self.attention(dec_hidden, # shape=(16, 300)
enc_output) # shape=(16, 200, 300)
for t in range(dec_tar.shape[1]): # 40(dec_maxlen)
# Teachering Forcing
x_input = dec_inp[:,t]
_, pred, dec_hidden = self.decoder(tf.expand_dims(x_input,1),
dec_hidden,
enc_output,
context_vector)
#此处的attention就是第二次进Attention的值u,其实就是正常开始训练之后的值,不同于初始化的值
context_vector, attn_dist = self.attention(dec_hidden, enc_output)
predictions.append(pred)
attentions.append(attn_dist)
return tf.stack(predictions, 1), dec_hidden
优化器选用Adam:
optimizer = tf.keras.optimizers.Adam(name='Adam', learning_rate=params["learning_rate"])
损失函数为交叉熵函数:
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False, reduction='none')
# 定义损失函数
def loss_function(real, pred):
mask = tf.math.logical_not(tf.math.equal(real, 1))
dec_lens = tf.reduce_sum(tf.cast(mask, dtype=tf.float32), axis=-1)
loss_ = loss_object(real, pred)
mask = tf.cast(mask, dtype=loss_.dtype)
loss_ *= mask
loss_ = tf.reduce_sum(loss_, axis=-1)/dec_lens
return tf.reduce_mean(loss_)
定义训练的每一步:
#dec_tar是标签值,dec_inp是正常输入值
def train_step(enc_inp, dec_tar, dec_inp):
with tf.GradientTape() as tape:
#初始化encoder状态,一堆0
enc_output, enc_hidden = model.call_encoder(enc_inp)
#初始化decoder状态等于encoder状态
dec_hidden = enc_hidden
# start index
#得到50步堆接起来的概率分布值,传入loss计算函数
#将encoder后的输出以及dec的输入和初始状态的隐藏层状态传入seq2seq模型,得到预测值
pred, _ = model(enc_output, # shape=(16, 200, 300)
dec_inp, # shape=(16, 40)
dec_hidden, # shape=(16, 300)
dec_tar) # shape=(16, 40)
loss = loss_function(dec_tar, pred)
#反向梯度更新
variables = model.encoder.trainable_variables + model.attention.trainable_variables + model.decoder.trainable_variables
gradients = tape.gradient(loss, variables)
optimizer.apply_gradients(zip(gradients, variables))
return loss
训练并保存中间结果:
best_loss = 20
epochs = params['epochs']
for epoch in range(epochs):
t0 = time.time()
step = 0
total_loss = 0
#分批次训练
for batch in dataset:
loss = train_step(batch[0]["enc_input"],
batch[1]["dec_target"],
batch[1]["dec_input"])
step += 1
total_loss += loss
if step % 100 == 0:#100
print('Epoch {} Batch {} Loss {:.4f}'.format(epoch + 1, step, total_loss / step))
#存模型数据,每一步存一下
if epoch % 1 == 0:
if total_loss / step < best_loss:
best_loss = total_loss / step
ckpt_save_path = ckpt_manager.save()
print('Saving checkpoint for epoch {} at {} ,best loss {}'.format(epoch + 1, ckpt_save_path, best_loss))
print('Epoch {} Loss {:.4f}'.format(epoch + 1, total_loss / step))
print('Time taken for 1 epoch {} sec\n'.format(time.time() - t0))
相关代码主要见train_helper.py和train_eval_test.py。
测试的主要流程与训练相似,可以选择greedy_decode和beam_decode两种方式:
def test(params):
assert params["mode"].lower() == "test", "change training mode to 'test' or 'eval'"
# assert params["beam_size"] == params["batch_size"], "Beam size must be equal to batch_size, change the params"
print("Building the model ...")
model = SequenceToSequence(params)
print("Creating the vocab ...")
vocab = Vocab(params["vocab_path"], params["vocab_size"])
print("Creating the batcher ...")
b = batcher(vocab, params)
print("Creating the checkpoint manager")
checkpoint_dir = "{}/checkpoint".format(params["seq2seq_model_dir"])
ckpt = tf.train.Checkpoint(SequenceToSequence=model)
ckpt_manager = tf.train.CheckpointManager(ckpt, checkpoint_dir, max_to_keep=5)
# path = params["model_path"] if params["model_path"] else ckpt_manager.latest_checkpoint
# path = ckpt_manager.latest_checkpoint
ckpt.restore(ckpt_manager.latest_checkpoint)
print("Model restored")
# for batch in b:
# yield batch_greedy_decode(model, batch, vocab, params)
#if params['greedy_decode']:
# params['batch_size'] = 512
predict_result(model, params, vocab, params['test_save_dir'])
def predict_result(model, params, vocab, result_save_path):
dataset = batcher(vocab, params)
# 预测结果
if params['greedy_decode']:
results = greedy_decode(model, dataset, vocab, params)
elif params['beam_search_decode']:
results = beam_decode(model, dataset, vocab, params)
results = list(map(lambda x: x.replace(" ",""), results))
#print("results2",results)#64个元素,每个元素都是大小为dec_max_len的序列
# 保存结果
save_predict_result(results, params)
return results
具体实现方法可见test_helper.py。
评价指标Rouge-L
def evaluate(params):
gen = test(params)
reals = []
preds = []
with tqdm(total=params["max_num_to_eval"], position=0, leave=True) as pbar:
for i in range(params["max_num_to_eval"]):
trial = next(gen)
reals.append(trial.real_abstract)
preds.append(trial.abstract)
pbar.update(1)
r = Rouge()
scores = r.get_scores(preds, reals, avg=True)
print("\n\n")
pprint.pprint(scores)
需要去看一下冠军团队的实现思路,学习好的经验与方法。此处待补充。
Awake是在setActive(true)时才会被调用,不过如果再setActive(false)然后重新setActive(true)的话,Awake就不会再被调用了,也就是说Awake能保证仅被调用一次。跟c++里构造函数的区别就是并不是Instantiate时候就开始调,不过可以凑合着当构造函数用。...
Idea Git 强制推送到远程分支 Idea Git Force push步骤是: push的时候,点击下拉 --- 选择: Force Push 即可
Ext JS - JavaScript framework for web apps | Sencha.comExt JS 4.2 官方文档地址 http://docs-devel.sencha.com/extjs/4.2.2/#!/api 搜索renderer即可查询API,Ext.grid.column.ColumnView.rendererrenderer : Function
1.右键“源文件”,点击“添加”-->“现有项”2.选择需要添加的文件,点击添加3.右键项目名称,点击“属性”4.选择“C/C++”下的“常规”5.点击“附加包含目录下”下的编辑,完成文件路径添加...
@RequestMapping(value = "/download") public void download(HttpServletRequest request,HttpServletResponse response) throws Exception { PageData pd = this.getPageData(); pd = printm...
转载提示:这是一篇旧文最近一直忙于面试,人事推给了我一份简历,职位是算法工程师,年龄是 46 岁,我揉了揉眼睛后再看看,确实是 46 岁。抱着忐忑的心,我电话面试一番后,还是不觉得他和我们的团队很适合。人都会有同理心,尤其是这么大岁数的程序员还是为了生计来找工作,心还是会隐隐触痛。年龄是多数程序员的天敌,之前没有概念因为生活中样本较少,现在来了一个鲜明的例子,并且还需要自己亲手关闭一扇...
启动内核有8个分区,而我们的uboot只有4个分区。0x00000000-0x00040000 : "bootloader" //存放uboot0x00040000-0x00060000 : "params" //存放环境变量0x00060000-0x00260000 : "kernel" //存放内核0...
Cycle ISP: Real Image Restoration via Improved Data Synthesis谷歌去年发表了一篇文章:Unprocessing Images for Learned Raw Denoising,是关于如何构造逼近真实的数据来进行降噪的,在去年的文章里,研究者们主要是模拟了 ISP 中从 RAW 图到 sRGB 的过程,然后将 ISP 的过程逆转过来,从 sRGB 到 RAW,然后再在 RAW 域上添加噪声,从而构造出符合真实场景的噪声数据。今年谷歌又发表了一篇
在项目中要用到拖动效果,之前写了一点不够完善,今天做了一点优化,在以前的基础上可以相互拖动了,改动的地方不大,但为了不影响之前的效果,我还是重新记录一下.HTML和我之前记录的文章一样,我就不重复了。。。以下是js:var targetFlag=false;//判断从框中拖出去是否拖到了另一个框 $('.items li').draggable({ prox
本文转载自MSDN作者:Stephen Cleary原文地址:https://msdn.microsoft.com/en-us/magazine/dn802603.aspx大多数有关 async/await 的在线资源假定您正在开发客户端应用程序,但在服务器上有 async 的位置吗?可以非常肯定地回答“有”。本文是对 ASP.NET 上异步请求的概念性概述,并提供了对最佳在线资源的引用...
传送门大意:向三维空间中加点,询问一个三维区间中点的个数。解题思路:带修改CDQ,将修改和询问一起插入CDQ分治询问。(询问可以由8个前缀和加减操作实现)其中第一层CDQ维护x有序。第二层CDQ维护y有序并且将z离线处理完更新答案。注意要将原数组的辅助数组推入第二层CDQ否则会将顺序毁坏。代码: 1 #include<cstdio> ...
这也是一个面向对象的问题,作为实例对象的你需要一步一步去攻城拔寨。待各处插满了自己的旗帜,回过头来,你会感谢一路走来的自己!一、入门 面向对象编程基础(类和继承,变量和方法的作用域,MVC基本概念,分类)方法和函数(消息,类定义和属性,与C/C++的混合编程)内存管理(strong/weak, ARC自动引用计数,自动释放对象)代码设计模式(Block块语句,Target/a...