不到1美元!1小时内训练自己的中文词向量

自然语言处理NLP是人工智能非常热门的一个领域,采用NLP技术构建的语言模型可以帮助业务系统提供更丰富的语言洞察和交互能力,譬如构建自己的智能客服、对于用户评价进行情感分析、舆情分析等。客户想要构建出自己的语言模型面临着诸多挑战,其中之一就是训练自己语言模型的词向量,因为词向量是构建各种业务语言模型的基础,但是面对海量的语料库,需要海量训练计算资源的时候,往往需要很大的技术、资源、时间的投入,代价很大。

Amazon SageMaker是AWS上一个托管的机器学习平台服务,能够帮助客户高效的自动标注数据、训练和部署机器学习模型。Amazon SageMaker内置了二十多种AWS优化过的机器学习高性能算法,包括常见的线性回归、神经网络、时间序列等,并且还支持MXNet、TensorFlow、PyTorch等主流深度学习框架。今天我们将介绍如何使用Amazon SageMaker服务的BlazingText在一小时内快速训练wiki中文的词向量。

Amazon SageMaker BlazingText 算法提供了 Word2vec 和文本分类算法的高度优化的实现。利用 BlazingText 算法,您可以轻松扩展到大型语言数据集。与 Word2vec 类似,它提供了 Skip-gram 和持续单词袋 (CBOW) 两种训练架构来训练词向量,称之为(WordEmbedding),并且还实现了监督学习的fastText文本分类器,您可以使用多台CPU服务器器 或 GPU 服务器在几分钟内对超过 10 亿个单词的模型进行训练。

管理 AWS 资源

登录控制台

观看视频开始学习:

SageMaker BlazingText算法讲解


1. 首先在AWS控制台进入Amazon SageMaker服务,如下图点击右上方黄色菜单“Create notebook instance”, 创建一个Jupyter notebook实例。

(单击以缩放)


2. 在配置Jupyter notebook实例页面输入实例名称,以及您所需要的实例的机器配置,这里选择ml.c5.2xlarge配置, 这是一台8vCPU 16G内存的机器,采用用Intel Sky-lake白金处理器,主频高达3.1GHz, 按秒计费。当然您也可以选择其他硬件配置的notebook,甚至是带Nvida V100的GPU配置。

(单击以缩放)


3. 在配置过程中您需要给Amazon SageMaker创建一个角色,使得Amazon SageMaker服务能够有权限访问S3存储桶上的wiki中文训练数据,并把训练好的模型上传到S3存储桶中。点击“Create a new role”系统会帮您自动创建,在测试环境下您可以选择“Any S3 bucket”,代表Amazon SageMaker服务可以访问任意一个S3存储桶,生产环境建议指定某个具体存储桶。

(单击以缩放)

(单击以缩放)


4. 创建Notebook实例大约需要几分钟的时间,我们可以在这期间在S3上创建一个存储桶,用于存放wiki中文的训练数据和模型。大约几分钟之后Amazon SageMaker就创建好了Notebook实例,点击“Open Jupyter”将自动跳转到Notebook页面。

(单击以缩放)


5. 因为我们对中文进行训练,在分析中会用到matplot画一些图显示中文,但notebook实例上默认没有安装中文字体,需要给notebook实例安装一个中文字体,譬如Simhei。点击右上角“New”,选择“Terminal”就可以进入Notebook 命令行界面进行字体安装。

(单击以缩放)

(单击以缩放)


因为Notebook实例中已经预先安装好了常用的Anaconda环境,如MxNet,TensorFlow等。我们点击右上角“New”,“Conda_Python3”创建一个Python3。

(单击以缩放)


2. 首先我们加载相应的Python库,并将我们之前创建的S3存储桶用于存放wiki中文训练数据的S3存储桶

 

importsagemaker  

fromsagemaker import get_execution_role  

importboto3  

importjson  


sess = sagemaker.Session()  


role = get_execution_role()  

print(role) # This is the role that SageMaker would use to leverage AWS resources (S3, CloudWatch) on your behalf


bucket = '421710401846-sagemaker-us-west-2'# Replace with your own bucket name if needed  

prefix = 'nlp-handson'#Replace with the prefix under which you want to store the data if needed 

3. 接下来我们下载wiki中文的中文数据集,原始数据是一个 xml压缩包,经过了简单的预处理后并被转换为简体中文,总大小 1.3G,包含约447万行,4.5亿个中文字。

!wget https://421710401846-sagemaker-us-west-2.s3-us-west-2.amazonaws.com/nlp-handson/zh-train/wiki_zh_full  

4. 下载后的中文数据集合需要进行清洗,主要两个清洗步骤

  • 原始数据包含许多英文单词,阿拉伯数字和标点符号,删除这些噪音信息
  • 使用 Jieba分词工具解析中文单词,以空格分开。因为在解析单词时,我们会遇到很多毫无意义的中文词,如 “的、有然、因为”,我们称这些单词停止词(Stopwords),也需要删除
# Install jieba tool

!pip install jieba  

# Download stopwords

!wget https://421710401846-sagemaker-us-west-2.s3-us-west-2.amazonaws.com/nlp-handson/zhstopwords.txt  


importlogging,jieba,os,re  


defget_stopwords():  

basicConfig(format='%(asctime)s:%(levelname)s:%(message)s',level=logging.INFO)  

#加载停用词表  

stopword_set = set()  

with open("zhstopwords.txt",'r',encoding="utf-8") as stopwords:  

for stopword in stopwords:  

add(stopword.strip("\n"))  

return stopword_set

5. 这里我们定义一个分词方法parse_zh_words,对原始wiki中文数据进行分词,以空格隔开,并把分词结果存到本地文件。我们使用的是8 vCPU,16G RAM的C5 notebook实例,整个清洗过程大约20分钟左右,清洗完后的wiki中文语料库大约966MB,这就是我们需要的的训练数据。

defparse_zh_words(read_file_path,save_file_path):   

file = open(read_file_path,"r",encoding="utf-8")  

#过滤掉英文和数字等特殊字符  

r1 = '[^\u4e00-\u9fa5]'

#写文件  

output = open(save_file_path,"w+",encoding="utf-8")  

content_line = file.readline()  

#获取停用词表  

stopwords = get_stopwords()  

#定义一个字符串变量,表示一篇文章的分词结果  

article_contents = "" 


while content_line:  

content_line = content_line.strip("\n")  

if len(content_line) > 0:  

#去除数字和英文等特殊字符  

zh_content_line = re.sub(r1, '', content_line)  

#使用jieba进行分词  

words = jieba.cut(zh_content_line,cut_all=False)  

……


input_file = './data/wiki_zh_full'

output_file = './data/wiki_zh_corpus'

parse_zh_words(input_file, output_file)  

6. 清洗完成之后我们可以查看一下清洗完后的文本是不是已经分好词,并且没有其他英文特殊符号。下面的脚本可以看到语句已经被空格分割成一个个中文单词了。

(单击以缩放)


7. 我们将清洗好的wiki中文语料库上传到S3的存储桶中,并定义BlazingText训练任务的S3 数据通道和模型输出位置。

train_channel = prefix + '/zh-train'


upload_data(path='data/wiki_zh_corpus', bucket=bucket, key_prefix=train_channel)  


s3_train_data = 's3://{}/{}'.format(bucket, train_channel)  

s3_output_location = 's3://{}/{}/zh-output'.format(bucket, prefix)  

8. 接下来使用BlazingText算法进行中文词向量的模型训练。BlazingText除了skip-gram和 CBOW 之外,还支持 “batch skip-gram” 模式,该模式使用高效的迷你批处理和矩阵矩阵操作(BLAS Level 3 routines)。 还支持跨多个 CPU 节点的分布式 Word2vec 训练,加快训练的速度。在不同类型的实例上,BlaingText 支持以下模式:

(单击以缩放)


9. 我们定义一个模型训练器(Estimator),其中BlazingText算法AWS已经封装在一个Docker容器中,容器存放在一个固定的ECR镜像仓库中,我们可以直接使用。在模型训练器中我们指定了需要什么样的机器配置,以及用于训练机器数量等参数。在这里我们选择了拥有最新的Nvida V100 GPU的P3实例。值得一提的是红色参数部分,Amazon SageMaker现在支持使用AWS Spot实例进行训练,Spot实例只有按需实例30%左右的价格,能够极大节约您的训练成本。

container = sagemaker.amazon.amazon_estimator.get_image_uri(region_name, "blazingtext", "latest")

bt_model = sagemaker.estimator.Estimator(container,  

role,   

train_instance_count=1,   

train_instance_type='ml.p3.2xlarge',  

train_use_spot_instances = True,  

train_volume_size = 10,  

train_max_run = 3600,  

train_max_wait = 3600,

input_mode= 'File',  

output_path=s3_output_location,  

sagemaker_session=sess)  

10. 之后继续配置训练过程中的超参数,我们使用skipgram模式,学习因子设置为0.05、负采样率为5等,将词向量目标维度vector_dim设置成300维,然后定义训练数据通道data_channel,这个将指向我们之前创建的S3存储桶。

set_hyperparameters(mode= "skipgram", #"batch_skipgram",

epochs=10,  
min_count=5,  
sampling_threshold=0.0001,  
learning_rate=0.05,  
window_size=5,  
vector_dim=300,  
negative_samples=5,  
batch_size=11, 
evaluation=False
subwords=False) 

train_data = sagemaker.session.s3_input(s3_train_data, distribution='FullyReplicated', content_type='text/plain', s3_data_type='S3Prefix')  
data_channels = {'train': train_data} 

11. 以上设置完成之后,只需要调用fit方法,就可以自动进行模型训练了。在这个fit方法背后,SageMaker会根据之前的配置自动下载BlazingText算法的容器,自动启动运行算法的GPU服务器,自动训练并打印出训练信息,最后自动吧训练好的wiki中文词向量模型存储到S3中,所有的一切都是自动化完成。

fit(inputs=data_channels, logs=True) 

11. 训练完后您会看到整个训练过程的用时,在训练阶段用时365.86秒,训练+上传模型共973秒,大约16分钟,但因为我们使用Spot机型,所以计费只有292秒,节约了大约70%的成本。训练好的模型文件Amazon SageMaker会自动上传到之前指定的S3存储桶中,去查看一下,模型大约1.1G左右。

(单击以缩放)

(单击以缩放)

1. 首先我们把模型文件下载下来并解压缩,得到一个vectors.txt文件,大约1.5G,这个文件就是wiki中文的词向量文件,包含了大约56.7万个词向量。我们把vectors.txt的前400个中文词向量进行二维可视化,我们使用TSNE降维方式。

frommanifold importTSNE  

frommatplotlib import pylab  

frompylab import mpl  


tsne = TSNE(perplexity=40, n_components=2, init='pca', n_iter=10000)  

two_d_embeddings = tsne.fit_transform(word_vecs[:num_points])  

labels = index_to_word[:num_points]  


rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体

rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题

%matplotlib inline  


defplot(embeddings, labels):  

figure(figsize=(20,20))  

fori, label in enumerate(labels):  

x, y = embeddings[i,:]  

scatter(x, y)  

annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points',ha='right', va='bottom')  

#pylab.savefig('tsne-ch.png')

show()  


plot(two_d_embeddings, labels)  

(单击以缩放)


2. 二维可视化如下图所示,我们可以看到类似的中文单词被归类到一起,说明词向量的训练明效果还不错。因为之前安装了Simhei字体,所以画布上能显示中文。

(单击以缩放)


3. 训练出来的vectors.txt是一个词向量文件,可以与 Gensim 和 Spacy 等其他工具存储格式兼容。因此我们使用另一个Python工具Gensim来验证。Gensim是一个用于从文档中自动提取语义主题的Python库。看看中文单词的类比性和相似性。可以看到,[女人,国王]对应了[男人,王后]的类比性比较,和牛肉不属于[天才,傻瓜,疯子]范畴相似性比较。或者也可以使用wordsim-240、wordsim-297中文数据集进行相似度评测,CA8 数据集进行类比度评测。

!pip install gensim  

frommodels importKeyedVectors  


word_vectors = KeyedVectors.load_word2vec_format('vectors.txt', binary=False)  


result1 = word_vectors.most_similar(positive=['女人', '国王'], negative=['男人'])  

print("{}: {:.4f}".format(*result1[0]))  


print(word_vectors.doesnt_match("天才傻瓜 疯子 牛肉".split())) 

(单击以缩放)


4. 我们还可以用wordcloud绘制一个中文词相似度云,把和某个中文单词相似的前100个词语都绘制在一个圆形的词云中。

!pip install wordcloud  

importpyplot as plt  

fromwordcloud import WordCloud  


...  


#输入一个词找出相似的前100个词

one_corpus = ["美丽"]  

result = word_vectors.most_similar(one_corpus[0],topn=100)  

#将返回的结果转换为字典,便于绘制词云

word_cloud = dict()  

forsim inresult:  

word_cloud[sim[0]] = sim[1]  

#绘制词云

draw_word_cloud(word_cloud) 

5. 可以看到类似如下的单词词云,三个图片分别代表了中文的形容词、名词和动词。

(单击以缩放)


6. 最后看一下成本,我们在notebook中我们不到一个小时内基于wiki中文上亿数量级的中文文本训练出了自己56.7万个中文单词的词向量,并且验证效果还不错。现在我们以美西俄勒冈区域为例:

     notebook实例C5 2xlarge $0.538/小时,计费使用一小时;

     GPU训练实例 P3 2xlarge $4.284/小时,计费使用292秒;

     S3 存储成本 $0.023 每GB/月,存储大约3GB;

     总成本Cost = 0.538*1 + (4.284/3600)*292 + (0.023*3)/30 = 0.88778,

连1美元都不到!出乎意料的实现了我们的目标!


总结一下,利用Amazon SageMaker内置的BlazingText算法,可以帮助客户多快好省的训练自己的语言模型词向量。不需要自己写算法,不用担心算力、不用担心成本,只要准备好您的数据,SageMaker都将自动化的构建各种业务的语言模型,为系统提供简单、高效的NLP AI能力集成。