内容简介:Word2Vec有两种训练方法:
Word2Vec模型
Word2Vec有两种训练方法: CBOW 和 Skip-gram 。CBOW的核心思想是上下文预测某个单词,Skip-gram正好相反,输入单词,要求网络预测它的上下文。
如上图所示,一个单词表达成 word embedding 后,很容易找到词义相近的其它词汇。
word embedding使用:句子中的单词以 one-hot 的形式作为输入,然后乘以学好的 word embedding 矩阵 Q ,就直接取出单词对应的 word embedding 了。 word embedding 矩阵 Q 其实就是网络 one-hot 层到 embedding 层映射的网络参数矩阵。所以, word embedding 等价于把 one-hot 层到 embedding 层的网络用预训练好的参数矩阵 Q 初始化了。不过, word embedding 只能初始化第一层网络参数,再高层就无能为力了。下游 NLP 任务使用 word embedding 的时候与图像类似,有两种方法,一种是 frozen:word embedding 层的网络参数固定不动;另一种是 fine-tuning ,即: word embedding 这层参数使用新的训练集合训练。
word embedding存在的问题
如图所示,多义词bank有两种含义,但是word embedding在对bank这个词进行编码的时候无法区分这两个含义。尽管上下文环境中出现的单词相同,但是在语言模型训练的时候,不论什么上下文的句子经过word2vec,都是预测相同的单词bank,而同一个单词占用的同一行的参数空间,这导致两种不同的上下文信息都会编码到相同的word embedding空间去。所以,word embedding无法区分多义词的不同语义,这是它一个比较严重的问题。
Bert
Bert采用 transformer 作为特征提取器,并采用双向语言模型。此外,Bert预训练的数据规模非常庞大。
NLP的四大类任务:
- 序列标注:中文分词,词性标注,命名实体识别,语义角色标注等。特点是,句子中的每个单词要求模型根据上下文给出一个分类类别。
- 分类任务:文本分类,情感计算。特点是,不管文章有多长,总体给出一个分类类别即可。
- 句子关系判断:问答,语义改写,自然语言推理等任务。特点是,给定两个句子,模型判断两个句子是否具有某种语义关系。
- 生成式任务:机器翻译,文本摘要,写诗造句,看图说话等。特点是,输入文本后,需要自主生成另外一种文字。
- 对于句子关系类任务,加上一个其实符号和终结符号,句子之间加上分隔符号即可。对输出来说,把第一个起始符号对应的 transformer 最后一层位置上串联一个 softmax 分类层。
- 对于分类问题,增加起始和终结符号,输出部分和句子关系类任务类似。
- 序列标注问题:输入部分和单句分类问题一样,只需要输出部分 transformer 最后一层每个单词对应位置都进行分类即可。
从这里可以看出, NLP 四大类任务都可以比较方便的改造 bert 能够接受的方式,这意味着 bert 具有很强的普适性。
bert构造双向语言模型
Masked双向语言模型,随机选择语料中15%的单词,其中80%替换成mask标记,10%随机替换另一个单词,10%不做改动。
Next Sentence Prediction,分两种情况选择句子,一种是在语料中选择真正顺序相连的两个句子;另一种方式是,第二个句子随机选择拼接到第一个句子的后面。要求模型做句子关系预测,判断第二个句子是不是真的第一个句子的后续句子。这么做的目的是,在很多NLP任务中是句子关系判断任务,单词预测颗粒度的训练到不了句子关系这个层级,增加这个任务有助于下游句子关系判断任务。由此可以看到,bert的预训练是个多任务过程。
bert输入部分处理
bert输入是一个线性序列,两个句子通过分隔符分割,前后两端分别增加标识符号。每个单词有三个embedding。
- 位置embedding:NLP中单词顺序是重要特征,需要对位置进行编码。
- 单词embedding
- 句子embedding:前面提到的训练数据都是由两个句子构成,那么每个句子有个句子整体的embedding对应每个单词。
三者叠加,就形成了bert的输入。
bert输出处理
bert评价
从模型或者方法的角度来看,bert借鉴了ELMO,GPT以及CBOW,主要提出了masked语言模型和next sentence prediction。训练采用两阶段模型,第一阶段双向语言模型预训练,第二阶段采用具体任务fine-tuning或者做特征集成;第二是特征提取采用transformer作为特征提取器而不是rnn或cnn。bert最大的两点是效果好普适性强,几乎所有的NLP任务都可以套用bert这种两阶段解决思路。
案例实现:Predicting Movie Review Sentiment with BERT on TF Hub
bert已经添加到TF-Hub模块,可以快速集成到现有项目中。bert层可以替代之前的elmo,glove层,并且通过fine-tuning,bert可以同时提供精度,训练速度的提升。
此案例中,我们将在tensorflow中使用bert训练一个模型用于判断电影评论的情绪是消极还是积极。
导入模块
from sklearn.model_selection import train_test_split import pandas as pd import tensorflow as tf import tensorflow_hub as hub from datetime import datetime
!pip install bert-tensorflow
import bert from bert import run_classifier from bert import optimization from bert import tokenization
数据下载
# 读取文件,创建dataframe def load_directory_data(directory): data={} data['sentence']=[] data['sentiment']=[] for file_path in os.listdir(directory): with tf.gfile.GFile(os.path.join(directory,file_path),'r') as f: data['sentence'].append(f.read()) data['sentiment'].append(re.match('\d+_(\d+)\.txt',file_path).group(1)) return pd.DataFrame.from_dict(data) # 添加新列,并打乱数据 def load_dataset(directory): # 积极情绪文件 pos_df=load_directory_data(os.path.join(directory,'pos')) # 消极情绪 neg_df=load_directory_data(os.path.join(directory,'neg')) pos_df['polarity']=1 neg_df['polarity']=0 # sample 参数frac,返回的比例,如:df中有10行数据,想返回30%,设置值为:0.3 return pd.concat([pos_df,neg_df]).sample(frac=1).reset_index(drop=True) # 下载并加载数据 def download_and_load_datasets(force_download=False): dataset=tf.keras.utils.get_file( fname='aclImdb.tar.gz', origin='http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz', extract=True ) train_df=load_dataset(os.path.join(os.path.dirname(dataset),'aclImdb','train')) test_df=load_dataset(os.path.join(os.path.dirname(dataset),'aclImdb','test')) return train_df,test_df train,test=download_and_load_datasets() # 去前5000个样本 train=train.sample(5000) test=test.sample(5000) DATA_COLUMN='sentence' LABEL_COLUMN='polarity' label_list=[0,1]
数据处理
我们需要将数据转换成bert能够处理的格式。我们首先创建 InputExample 构造函数:
- test_a:我们要分类的数据,如:DATA_COLUMN
- test_b:用于句子关系判断,如:问答,翻译等
- label:数据标签
train_InputExample=train.apply(lambda x:bert.run_classifier.InputExample( guid=None, text_a=x[DATA_COLUMN], text_b=None, label=x[LABEL_COLUMN] ),axis=1) test_InputExample=test.apply(lambda x:bert.run_classifier.InputExample( guid=None, text_a=x[DATA_COLUMN], text_b=None, label=x[LABEL_COLUMN] ),axis=1)
接下来,我们需要处理数据以适合bert进行训练。步骤依次如下:
- 单词全部小写
- 将文本转换成序列(如:‘sally says hi’ -> ['sally','says','hi'])
- 将单词分解为wordpieces(如:‘calling’->['call','##ing'])
- 用bert提供的词汇文件进行单词索引映射
- 添加‘CLS’,'SEP'标记符
- 每次输入添加‘index’和‘segment’标记
# lowercase bert版本 BERT_MODEL_HUB='https://tfhub.dev/google/bert_uncased_L-12_H-768_A-12/1' # 获取词表文件,小写数据处理 def create_tokenizer_from_hub_module(): with tf.Graph().as_default(): bert_module = hub.Module(BERT_MODEL_HUB) tokenization_info = bert_module(signature='tokenization_info', as_dict=True) with tf.Session() as sess: vocab_file,do_lower_case=sess.run([tokenization_info['vocab_file'],tokenization_info['do_lower_case']]) return bert.tokenization.FullTokenizer( vocab_file=vocab_file, do_lower_case=do_lower_case ) tokenizer=create_tokenizer_from_hub_module() # 序列最长 MAX_SEQ_LENGTH=128 # 将训练,测试数据特征转换成bert需要的格式 train_features=bert.run_classifier.convert_examples_to_features(train_InputExample,label_list,MAX_SEQ_LENGTH,tokenizer) test_features=bert.run_classifier.convert_examples_to_features(test_InputExample,label_list,MAX_SEQ_LENGTH,tokenizer)
创建模型
def create_model(is_predicting,input_ids,input_mask,segment_ids,labels,num_labels): # 创建分类模型 bert_module=hub.Module( BERT_MODEL_HUB, trainable=True ) bert_inputs=dict( input_ids=input_ids, input_mask=input_mask, segment_ids=segment_ids ) bert_outputs=bert_module( inputs=bert_inputs, signature='tonkens', as_dict=True ) output_layer=bert_outputs['pooled_output'] hidden_size=output_layer.shape[-1].value output_weights=tf.get_variable( 'output_weights',[num_labels,hidden_size], initializer=tf.truncated_normal_initializer(stddev=0.02) ) output_bias=tf.get_variable( 'output_bias',[num_labels],initializer=tf.zeros_initializer() ) with tf.variable_scope('loss'): # dropout用于防止过拟合,仅训练时使用 output_layer=tf.nn.dropout(output_layer,keep_prob=0.9) logits=tf.matmul(output_layer,output_weights,transpose_b=True) logits=tf.nn.bias_add(logits,output_bias) log_prob=tf.nn.log_softmax(logits,axis=-1) # 将标签转为one-hot格式 one_hot_labels=tf.one_hot(labels,depth=num_labels) predcited_labels=tf.squeeze(tf.argmax(log_prob,axis=-1)) if is_predicting: return (predcited_labels,log_prob) per_example_loss=-tf.reduce_sum(one_hot_labels*log_prob,axis=-1) loss=tf.reduce_mean(per_example_loss) return (loss,predcited_labels,log_prob)
创建InputFn
def model_fn_builder(num_labels,learning_rate,num_train_steps,num_warmup_steps): def model_fn(features,labels,mode,params): input_idx=features['input_idx'] input_mask=features['input_mask'] segment_ids=features['segment_ids'] lable_ids=features['labels_ids'] is_predicting=(mode == tf.estimator.ModeKeys.PREDICT) if not is_predicting: (loss,predicted_labels,log_probs)=create_model( is_predicting,input_idx,input_mask,segment_ids,lable_ids,num_labels ) train_op = bert.optimization.create_optimizer( loss, learning_rate, num_train_steps, num_warmup_steps, use_tpu=False) def metric_fn(label_ids,predicted_labels): accuracy=tf.metrics.accuracy(labels=lable_ids,predictions=predicted_labels) f1_score = tf.contrib.metrics.f1_score( label_ids, predicted_labels) auc = tf.metrics.auc( label_ids, predicted_labels) recall = tf.metrics.recall( label_ids, predicted_labels) precision = tf.metrics.precision( label_ids, predicted_labels) true_pos = tf.metrics.true_positives( label_ids, predicted_labels) true_neg = tf.metrics.true_negatives( label_ids, predicted_labels) false_pos = tf.metrics.false_positives( label_ids, predicted_labels) false_neg = tf.metrics.false_negatives( label_ids, predicted_labels) return { "eval_accuracy": accuracy, "f1_score": f1_score, "auc": auc, "precision": precision, "recall": recall, "true_positives": true_pos, "true_negatives": true_neg, "false_positives": false_pos, "false_negatives": false_neg } eval_metrics=metric_fn(lable_ids,predicted_labels) if mode == tf.estimator.ModeKeys.TRAIN: return tf.estimator.EstimatorSpec( mode=mode, loss=loss, train_op=train_op ) else: return tf.estimator.EstimatorSpec( mode=mode, loss=loss, eval_metric_ops=eval_metrics ) else: (predicted_labels, log_probs) = create_model( is_predicting, input_idx, input_mask, segment_ids, lable_ids, num_labels) predictions = { 'probabilities': log_probs, 'labels': predicted_labels } return tf.estimator.EstimatorSpec(mode, predictions=predictions) return model_fn
参数配置
BATCH_SIZE=32 LEARNING_RATE=2e-5 NUM_TRAIN_EPOCHS=3 WARMUP_PROPORTION=0.1 SAVE_CHECKEPOINTS_STEPS=500 SAVE_SUMMARY_STEPS=100 # 计算train,warmup总训练步数 num_train_steps=int(len(train_features)/BATCH_SIZE*NUM_TRAIN_EPOCHS) num_warmup_steps=int(num_train_steps*WARMUP_PROPORTION) # 设置模型保存路径/次数,图信息保存次数 run_config=tf.estimator.RunConfig( model_dir=OUTPUT_DIR, save_checkpoints_steps=SAVE_CHECKEPOINTS_STEPS, save_summary_steps=SAVE_SUMMARY_STEPS )
模型训练,验证
modle_fn=model_fn_builder( num_labels=len(label_list), learning_rate=LEARNING_RATE, num_train_steps=num_train_steps, num_warmup_steps=num_train_steps ) estimator=tf.estimator.Estimator( model_fn=modle_fn, config=run_config, params={'batch_size':BATCH_SIZE} ) train_input_fn=bert.run_classifier.input_fn_builder( features=train_features, seq_length=MAX_SEQ_LENGTH, is_training=True, drop_remainder=False ) estimator.train(input_fn=train_input_fn,max_steps=num_train_steps)
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Spring 2企业应用开发
人民邮电出版社 / 2008-10 / 45.00元
《Spring 2企业应用开发》是一部权威的实战指南,由Spring开发团队撰写,全面讲述了Spring2企业应用开发。主要涵盖SpringFramework、核心容器、AOP(面向方面编程)、典型的SpringAOP框架等内容。通过阅读《Spring 2企业应用开发》,读者能够在实战中掌握Spring最佳实践,成为高效的Java开发人员。一起来看看 《Spring 2企业应用开发》 这本书的介绍吧!