文章目录

  • 简介
  • 0 前言
  • 1 项目介绍
  • 2 数据集介绍
  • 3 项目实现
    • 3.1 数据预处理
    • 3.2 配置网络
    • 3.3 网络训练
    • 3.4 模型评估
  • 4 最后-毕设帮助

简介

今天学长向大家介绍NLP基础

NLP:词向量Skip-gram word2vec

毕设帮助,开题指导,资料分享,疑问解答(见文末)

0 前言

今天,学长给大家介绍NLP中应用非常广泛的一个领域,当然这个应用已经很成熟了——文本处理,在文本处理中,其中最为核心的就是Word2vec模型,我们通过text8数据集对word2vec中的Skip-gram进行实战。

1 项目介绍

2013年,Google开源了一款用于词向量计算的工具——word2vec,引起了工业界和学术界的关注。首先,word2vec可以在百万数量级的词典和上亿的数据集上进行高效地训练;其次,该工具得到的训练结果——词向量(word embedding),可以很好地度量词与词之间的相似性。随着深度学习(Deep Learning)在自然语言处理中应用的普及,很多人误以为word2vec是一种深度学习算法。其实word2vec算法的背后是一个浅层神经网络。另外需要强调的一点是,word2vec是一个计算word vector的开源工具。当我们在说word2vec算法或模型的时候,其实指的是其背后用于计算word vector的CBoW模型和Skip-gram模型。接下来我们就是通过text8数据集进行word2vec中Skip-gram实战。

2 数据集介绍

我们本次项目使用的数据集是text8数据集,这个数据集里包含了大量从维基百科收集到的英文语料,我们可以通过代码下载数据集,下载后的文件被保存在当前目录的text8.txt文件内。当然,为了防止大家网速以及各种原因下载不成功数据集,本人将数据集上传了一份在百度网盘上,大家可以自行下载[验证码:40g3]。由于该数据集特别大,我们可以通过Linux中的查看末尾几行的命令大致看一下相关的数据集内容,命令如下:

3 项目实现

3.1 数据预处理

首先导入必要的第三方库

import io
import os
import sys
import requests
from collections import OrderedDict
import math
import random
import numpy as np
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Embedding

接下来就是数据预处理,首先,找到一个合适的语料用于训练word2vec模型。前面也说明了:我们选择text8数据集,这个数据集里包含了大量从维基百科收集到的英文语料,我们可以通过如下代码下载数据集,下载后的文件被保存在当前目录的text8.txt文件内。当然,我们也可以直接从本人给的数据集直接导入项目路径即可,具体实现如下:

#下载语料用来训练word2vec
def download():#可以从百度云服务器下载一些开源数据集(dataset.bj.bcebos.com)corpus_url = "https://dataset.bj.bcebos.com/word2vec/text8.txt"#使用python的requests包下载数据集到本地web_request = requests.get(corpus_url)corpus = web_request.content#把下载后的文件存储在当前目录的text8.txt文件内,选择自己项目所在的路径with open("./text8.txt", "wb") as f:f.write(corpus)f.close()
download()

接下来,把下载的语料读取到程序里,并打印前800个字符看看语料的样子,代码如下:

#读取text8数据
def load_text8():with open("./text8.txt", "r") as f:corpus = f.read().strip("\n")f.close()return corpus
corpus = load_text8()
#打印前800个字符,简要看一下这个语料的样子
print(corpus[:800])

打印效果如下:

一般来说,在自然语言处理中,需要先对语料进行切词。对于英文来说,可以比较简单地直接使用空格进行切词,其实也可以用第三方库nltk直接进行切分,为了让大家更好的学习这一块,因此,尽可能的自己写所对应的代码,实现如下:

#对语料进行预处理(分词)
def data_preprocess(corpus):#由于英文单词出现在句首的时候经常要大写,所以我们把所有英文字符都转换为小写,#以便对语料进行归一化处理(Apple vs apple等)corpus = corpus.strip().lower()corpus = corpus.split(" ")return corpus
corpus = data_preprocess(corpus)
print(corpus[:50])

打印效果如下:

在经过切词后,需要对语料进行统计,为每个词构造ID。一般来说,可以根据每个词在语料中出现的频次构造ID,频次越高,ID越小,便于对词典进行管理。代码如下:

#构造词典,统计每个词的频率,并根据频率将每个词转换为一个整数id
def build_dict(corpus):#首先统计每个不同词的频率(出现的次数),使用一个词典记录word_freq_dict = dict()for word in corpus:if word not in word_freq_dict:word_freq_dict[word] = 0word_freq_dict[word] += 1#将这个词典中的词,按照出现次数排序,出现次数越高,排序越靠前#一般来说,出现频率高的高频词往往是:I,the,you这种代词,而出现频率低的词,往往是一些名词,如:nlpword_freq_dict = sorted(word_freq_dict.items(), key = lambda x:x[1], reverse = True)    #构造3个不同的词典,分别存储,#每个词到id的映射关系:word2id_dict#每个id出现的频率:word2id_freq#每个id到词典映射关系:id2word_dictword2id_dict = dict()word2id_freq = dict()id2word_dict = dict()#按照频率,从高到低,开始遍历每个单词,并为这个单词构造一个独一无二的idfor word, freq in word_freq_dict:curr_id = len(word2id_dict)word2id_dict[word] = curr_idword2id_freq[word2id_dict[word]] = freqid2word_dict[curr_id] = wordreturn word2id_freq, word2id_dict, id2word_dict
word2id_freq, word2id_dict, id2word_dict = build_dict(corpus)
vocab_size = len(word2id_freq)
print("there are totoally %d different words in the corpus" % vocab_size)
for _, (word, word_id) in zip(range(50), word2id_dict.items()):print("word %s, its id %d, its word freq %d" % (word, word_id, word2id_freq[word_id]))

打印效果如下:

得到word2id词典后,我们还需要进一步处理原始语料,把每个词替换成对应的ID,便于神经网络进行处理,代码如下:

#把语料转换为id序列
def convert_corpus_to_id(corpus, word2id_dict):#使用一个循环,将语料中的每个词替换成对应的id,以便于神经网络进行处理corpus = [word2id_dict[word] for word in corpus]return corpus
corpus = convert_corpus_to_id(corpus, word2id_dict)
print("%d tokens in the corpus" % len(corpus))
print(corpus[:50])

打印结果如下:

接下来,需要使用二次采样法处理原始文本。二次采样法的主要思想是降低高频词在语料中出现的频次,降低的方法是将随机将高频的次抛弃,频率越高,被抛弃的概率就越高,频率越低,被抛弃的概率就越低,这样像标点符号或冠词这样的高频词就会被抛弃,从而优化整个词表的词向量训练效果,代码如下:

#使用二次采样算法(subsampling)处理语料,强化训练效果
def subsampling(corpus, word2id_freq):#这个discard函数决定了一个词会不会被替换,这个函数是具有随机性的,每次调用结果不同#如果一个词的频率很大,那么它被遗弃的概率就很大def discard(word_id):return random.uniform(0, 1) < 1 - math.sqrt(1e-4 / word2id_freq[word_id] * len(corpus))corpus = [word for word in corpus if not discard(word)]return corpus
corpus = subsampling(corpus, word2id_freq)
print("%d tokens in the corpus" % len(corpus))
print(corpus[:50])

打印结果如下:

在完成语料数据预处理之后,需要构造训练数据。根据上面的描述,我们需要使用一个滑动窗口对语料从左到右扫描,在每个窗口内,中心词需要预测它的上下文,并形成训练数据。在实际操作中,由于词表往往很大(50000,100000等),对大词表的一些矩阵运算(如softmax)需要消耗巨大的资源,因此可以通过负采样的方式模拟softmax的结果,代码实现如下。

  • 给定一个中心词和一个需要预测的上下文词,把这个上下文词作为正样本。
  • 通过词表随机采样的方式,选择若干个负样本。
  • 把一个大规模分类问题转化为一个2分类问题,通过这种方式优化计算速度。
#构造数据,准备模型训练
#max_window_size代表了最大的window_size的大小,程序会根据max_window_size从左到右扫描整个语料
#negative_sample_num代表了对于每个正样本,我们需要随机采样多少负样本用于训练,
#一般来说,negative_sample_num的值越大,训练效果越稳定,但是训练速度越慢。
def build_data(corpus, word2id_dict, word2id_freq, max_window_size = 3, negative_sample_num = 6):#使用一个list存储处理好的数据dataset = []center_word_idx=0#从左到右,开始枚举每个中心点的位置while center_word_idx < len(corpus):#以max_window_size为上限,随机采样一个window_size,这样会使得训练更加稳定window_size = random.randint(1, max_window_size)#当前的中心词就是center_word_idx所指向的词,可以当作正样本positive_word = corpus[center_word_idx]#以当前中心词为中心,左右两侧在window_size内的词就是上下文context_word_range = (max(0, center_word_idx - window_size), min(len(corpus) - 1, center_word_idx + window_size))context_word_candidates = [corpus[idx] for idx in range(context_word_range[0], context_word_range[1]+1) if idx != center_word_idx]#对于每个正样本来说,随机采样negative_sample_num个负样本,用于训练for context_word in context_word_candidates:#首先把(上下文,正样本,label=1)的三元组数据放入dataset中,#这里label=1表示这个样本是个正样本dataset.append((context_word, positive_word, 1))#开始负采样i = 0while i < negative_sample_num:negative_word_candidate = random.randint(0, vocab_size-1)if negative_word_candidate is not positive_word:#把(上下文,负样本,label=0)的三元组数据放入dataset中,#这里label=0表示这个样本是个负样本dataset.append((context_word, negative_word_candidate, 0))i += 1center_word_idx = min(len(corpus) - 1, center_word_idx + window_size)if center_word_idx == (len(corpus) - 1):center_word_idx += 1if center_word_idx % 100000 == 0:print(center_word_idx)return datasetdataset = build_data(corpus, word2id_dict, word2id_freq)
for _, (context_word, target_word, label) in zip(range(50), dataset):print("center_word %s, target %s, label %d" % (id2word_dict[context_word],id2word_dict[target_word], label))

打印结果如下

训练数据准备好后,把训练数据都组装成mini-batch,并准备输入到网络中进行训练,代码如下:

#构造mini-batch,准备对模型进行训练
#我们将不同类型的数据放到不同的tensor里,便于神经网络进行处理
#并通过numpy的array函数,构造出不同的tensor来,并把这些tensor送入神经网络中进行训练
def build_batch(dataset, batch_size, epoch_num):#center_word_batch缓存batch_size个中心词center_word_batch = []#target_word_batch缓存batch_size个目标词(可以是正样本或者负样本)target_word_batch = []#label_batch缓存了batch_size个0或1的标签,用于模型训练label_batch = []for epoch in range(epoch_num):#每次开启一个新epoch之前,都对数据进行一次随机打乱,提高训练效果random.shuffle(dataset)for center_word, target_word, label in dataset:#遍历dataset中的每个样本,并将这些数据送到不同的tensor里center_word_batch.append([center_word])target_word_batch.append([target_word])label_batch.append(label)#当样本积攒到一个batch_size后,我们把数据都返回回来#在这里我们使用numpy的array函数把list封装成tensor#并使用python的迭代器机制,将数据yield出来#使用迭代器的好处是可以节省内存if len(center_word_batch) == batch_size:yield np.array(center_word_batch).astype("int64"), \np.array(target_word_batch).astype("int64"), \np.array(label_batch).astype("float32")center_word_batch = []target_word_batch = []label_batch = []if len(center_word_batch) > 0:yield np.array(center_word_batch).astype("int64"), \np.array(target_word_batch).astype("int64"), \np.array(label_batch).astype("float32")# for _, batch in zip(range(10), build_batch(dataset, 128, 3)):
#     print(batch)

3.2 配置网络

定义skip-gram的网络结构,用于模型训练。在飞桨动态图中,对于任意网络,都需要定义一个继承自fluid.dygraph.Layer的类来搭建网络结构、参数等数据的声明。同时需要在forward函数中定义网络的计算逻辑。值得注意的是,我们仅需要定义网络的前向计算逻辑,飞桨会自动完成神经网络的反向计算,实现如下:

#定义skip-gram训练网络结构
#这里我们使用的是paddlepaddle的1.8.0版本
#一般来说,在使用fluid训练的时候,我们需要通过一个类来定义网络结构,这个类继承了fluid.dygraph.Layer
class SkipGram(fluid.dygraph.Layer):def __init__(self, vocab_size, embedding_size, init_scale=0.1):#vocab_size定义了这个skipgram这个模型的词表大小#embedding_size定义了词向量的维度是多少#init_scale定义了词向量初始化的范围,一般来说,比较小的初始化范围有助于模型训练super(SkipGram, self).__init__()self.vocab_size = vocab_sizeself.embedding_size = embedding_size#使用paddle.fluid.dygraph提供的Embedding函数,构造一个词向量参数#这个参数的大小为:[self.vocab_size, self.embedding_size]#数据类型为:float32#这个参数的名称为:embedding_para#这个参数的初始化方式为在[-init_scale, init_scale]区间进行均匀采样self.embedding = Embedding(size=[self.vocab_size, self.embedding_size],dtype='float32',param_attr=fluid.ParamAttr(name='embedding_para',initializer=fluid.initializer.UniformInitializer(low=-0.5/embedding_size, high=0.5/embedding_size)))#使用paddle.fluid.dygraph提供的Embedding函数,构造另外一个词向量参数#这个参数的大小为:[self.vocab_size, self.embedding_size]#数据类型为:float32#这个参数的名称为:embedding_para_out#这个参数的初始化方式为在[-init_scale, init_scale]区间进行均匀采样#跟上面不同的是,这个参数的名称跟上面不同,因此,#embedding_para_out和embedding_para虽然有相同的shape,但是权重不共享self.embedding_out = Embedding(size=[self.vocab_size, self.embedding_size],dtype='float32',param_attr=fluid.ParamAttr(name='embedding_out_para',initializer=fluid.initializer.UniformInitializer(low=-0.5/embedding_size, high=0.5/embedding_size)))#定义网络的前向计算逻辑#center_words是一个tensor(mini-batch),表示中心词#target_words是一个tensor(mini-batch),表示目标词#label是一个tensor(mini-batch),表示这个词是正样本还是负样本(用0或1表示)#用于在训练中计算这个tensor中对应词的同义词,用于观察模型的训练效果def forward(self, center_words, target_words, label):#首先,通过embedding_para(self.embedding)参数,将mini-batch中的词转换为词向量#这里center_words和eval_words_emb查询的是一个相同的参数#而target_words_emb查询的是另一个参数center_words_emb = self.embedding(center_words)target_words_emb = self.embedding_out(target_words)#center_words_emb = [batch_size, embedding_size]#target_words_emb = [batch_size, embedding_size]#我们通过点乘的方式计算中心词到目标词的输出概率,并通过sigmoid函数估计这个词是正样本还是负样本的概率。word_sim = fluid.layers.elementwise_mul(center_words_emb, target_words_emb)word_sim = fluid.layers.reduce_sum(word_sim, dim = -1)word_sim = fluid.layers.reshape(word_sim, shape=[-1])pred = fluid.layers.sigmoid(word_sim)#通过估计的输出概率定义损失函数,注意我们使用的是sigmoid_cross_entropy_with_logits函数#将sigmoid计算和cross entropy合并成一步计算可以更好的优化,所以输入的是word_sim,而不是predloss = fluid.layers.sigmoid_cross_entropy_with_logits(word_sim, label)loss = fluid.layers.reduce_mean(loss)#返回前向计算的结果,飞桨会通过backward函数自动计算出反向结果。return pred, loss

3.3 网络训练

完成网络定义后,就可以启动模型训练。我们定义每隔100步打印一次Loss,以确保当前的网络是正常收敛的。同时,我们每隔1000步观察一下skip-gram计算出来的同义词(使用 embedding的乘积),可视化网络训练效果,不过需要特别说明的是: 本次训练没有设置相应的暂停程序,需要我们在训练到差不多的准确度的时候就得手动停下来,否则会出现内存溢出的错误,这点需要我们特别的注意!!!! ,经过自己的经验,不过至少得训练差不多40万步左右才能看出效果,具体代码如下:

#开始训练,定义一些训练过程中需要使用的超参数
batch_size = 256
epoch_num = 10
embedding_size = 200
step = 0
learning_rate = 0.0002#定义一个使用word-embedding计算cos的函数
def get_cos(query1_token, query2_token, embed):W = embedx = W[word2id_dict[query1_token]]y = W[word2id_dict[query2_token]]cos = np.dot(x, y) / np.sqrt(np.sum(y * y) * np.sum(x * x) + 1e-9)flat = cos.flatten()print("单词1 %s 和单词2 %s 的cos结果为 %f" %(query1_token, query2_token, cos))#将模型放到GPU上训练(fluid.CUDAPlace(0)),如果需要指定CPU,则需要改为fluid.CPUPlace()
with fluid.dygraph.guard(fluid.CUDAPlace(0)):#通过我们定义的SkipGram类,来构造一个Skip-gram模型网络skip_gram_model = SkipGram(vocab_size, embedding_size)#构造训练这个网络的优化器adam = fluid.optimizer.AdamOptimizer(learning_rate=learning_rate, parameter_list=skip_gram_model.parameters())#使用build_batch函数,以mini-batch为单位,遍历训练数据,并训练网络for center_words, target_words, label in build_batch(dataset, batch_size, epoch_num):#使用fluid.dygraph.to_variable函数,将一个numpy的tensor,转换为飞桨可计算的tensorcenter_words_var = fluid.dygraph.to_variable(center_words)target_words_var = fluid.dygraph.to_variable(target_words)label_var = fluid.dygraph.to_variable(label)#将转换后的tensor送入飞桨中,进行一次前向计算,并得到计算结果pred, loss = skip_gram_model(center_words_var, target_words_var, label_var)#通过backward函数,让程序自动完成反向计算loss.backward()#通过minimize函数,让程序根据loss,完成一步对参数的优化更新adam.minimize(loss)#使用clear_gradients函数清空模型中的梯度,以便于下一个mini-batch进行更新skip_gram_model.clear_gradients()#每经过100个mini-batch,打印一次当前的loss,看看loss是否在稳定下降step += 1if step % 100 == 0:print("step %d, loss %.3f" % (step, loss.numpy()[0]))#经过10000个mini-batch,打印一次模型对eval_words中的10个词计算的同义词#这里我们使用词和词之间的向量点积作为衡量相似度的方法#我们只打印了5个最相似的词if step % 2000 == 0:embedding_matrix = skip_gram_model.embedding.weight.numpy()np.save("./embedding", embedding_matrix)get_cos("king","queen",embedding_matrix)get_cos("she","her",embedding_matrix)get_cos("topic","theme",embedding_matrix)get_cos("woman","game",embedding_matrix)get_cos("one","name",embedding_matrix)

训练过程如下:

从上面的训练的过程可以看出,当我们随着训练步数的不断增加,其训练的结果不断趋于稳定,这里很明显看出前三对单词的相似度还是挺好的,符合我们的预期,之所以第三对的相似度如此低,是因为训练语料还是不够大,训练的效果不是太好导致其出现在原文的次数较少,需要说明的是:本项目是用的余弦相似度进行计算的。另外的一个原因就是theme和topic本来的意思也比较远,我们可以用金山词霸查这两个词的意思:

具体的意思如下:

3.4 模型评估

我们用余弦相似度计算评价词向量结果,具体实现如下:

#定义一个使用word-embedding计算cos的函数
def get_cos(query1_token, query2_token, embed):W = embedx = W[word2id_dict[query1_token]]y = W[word2id_dict[query2_token]]cos = np.dot(x, y) / np.sqrt(np.sum(y * y) * np.sum(x * x) + 1e-9)flat = cos.flatten()print("单词1 %s 和单词2 %s 的cos结果为 %f" %(query1_token, query2_token, cos) )embedding_matrix = np.load('embedding.npy')
get_cos("king","queen",embedding_matrix)
get_cos("she","her",embedding_matrix)
get_cos("topic","theme",embedding_matrix)
get_cos("woman","game",embedding_matrix)
get_cos("one","name",embedding_matrix)

预测结果如下

训练的结果还是挺满意的,不过这里需要提醒大家的就是前面也提到过这个问题:那就是第三组单词的相似度较低,原因也从客观以及主观给大家分析了,需要大家留意一下,因此有相对低的结果也很正常。

到此为止,学长的本次项目讲解到此结束。

4 最后-毕设帮助

毕业设计 - NLP:词向量Skip-gram word2vec相关推荐

  1. NLP(词向量、word2vec和word embedding)

    最近在做一些文本处理相关的任务,虽然对于相关知识有所了解,而且根据相关开源代码也可以完成相应任务:但是具有有些细节,尤其是细节之间的相互关系,感觉有些模糊而似懂非懂,所以找到相关知识整理介绍,分享如下 ...

  2. NLP之词向量:利用word2vec对20类新闻文本数据集进行词向量训练、测试(某个单词的相关词汇)

    NLP之词向量:利用word2vec对20类新闻文本数据集进行词向量训练.测试(某个单词的相关词汇) 目录 输出结果 设计思路 核心代码 输出结果 寻找训练文本中与morning最相关的10个词汇: ...

  3. BERT实战(1):使用DistilBERT作为词嵌入进行文本情感分类,与其它词向量(FastText,Word2vec,Glove)进行对比

    这次根据一篇教程Jay Alammar: A Visual Guide to Using BERT for the First Time学习下如何在Pytorch框架下使用BERT. 主要参考了中文翻 ...

  4. 神经网络词向量模型之Word2Vec

    1.前言 基于one-hot对于词汇进行编码的方式着实让人头疼,又无奈.头疼的是占用空间很大,计算量大,无奈的是这么简单粗暴的方式居然一直流传到了现在.今天给大家介绍一款基于神经网络语言模型来生成词向 ...

  5. [NLP]高级词向量表达之Word2vec详解(知识点全覆盖)

    1.词表征(Word Representation) 首先明确句子是 序列化 ,里面携带了大量大信息.在NLP发展的进程里面, 采用了one-hot vector的形式来表示一个句子里面的词是一种方式 ...

  6. NLP系列(10)_词向量之图解Word2vec

    审校:龙心尘 作者:Jay Alammar 编译:张秋玥.毅航.高延 https://blog.csdn.net/longxinchen_ml/article/details/89077048 原文链 ...

  7. 【NLP】词向量:从word2vec、glove、ELMo到BERT详解!

    目前,词向量(又叫词嵌入word embedding)已经成为NLP领域各种任务的必备一步,而且随着bert elmo,gpt等预训练模型的发展,词向量演变为知识表示方法,但其本质思想不变.学习各种词 ...

  8. nlp中的词向量对比:word2vec/glove/fastText/elmo/GPT/bert

    本文以QA形式对自然语言处理中的词向量进行总结:包含word2vec/glove/fastText/elmo/bert. 目录 一.文本表示和各词向量间的对比  1.文本表示哪些方法? 2.怎么从语言 ...

  9. 词向量介绍以及Word2Vec的pytorch实现

    词向量 在自然语言处理任务中,首先需要考虑字.词如何在计算机中表示.通常,有两种表示方式:one-hot表示和分布式表示 one-hot表示 把每个词表示为一个长向量.这个向量的维度是词表大小,向量中 ...

  10. bert获得词向量_NLP中的词向量对比:word2vec/glove/fastText/elmo/GPT/bert

    作者:JayLou,NLP算法工程师 知乎专栏:高能NLP之路 https://zhuanlan.zhihu.com/p/56382372 本文以QA形式对自然语言处理中的词向量进行总结:包含word ...

最新文章

  1. 基于三维卷积神经网络的RGB-D显著目标检测
  2. double类型占几个字节_MongoDB 中的数据类型
  3. 信息学奥赛一本通——1001:Hello,World!
  4. 7-27 冒泡法排序 (20 分)
  5. CodeForces 314C 树状数组 + dp
  6. python创建虚拟环境报错typeerror_解决Python中报错TypeError: must be str, not bytes问题
  7. 【linux】BootLoader简介(c语言编程)
  8. java webengine_webview – JavaFX 8 WebEngine:如何从java到console.log()从java到System.out?
  9. Yii框架多表查询(一对一与多对一)
  10. 基于Android平台的三维实时全景地图设计与实现(一)
  11. 高洛峰细说php百度贴吧_高洛峰细说PHP视频教程推荐
  12. 【历史上的今天】5 月 30 日:Risc 架构之父出生;ATT 宣布推出视频电话系统;Windows NT 3.51 发布
  13. cas5.3.2单点登录-配置记住我(十六)
  14. Eureka Server报错:Retry limit reached; giving up on complet the request
  15. 高压水冷加热器行业研究及十四五规划分析报告
  16. 大疆FPGA/芯片开发工程师(A卷)笔试题(含详解)
  17. 保护海洋主题的微信公众号图文排版必备素材集锦
  18. 客流量总是少?是你门店选址出了问题!
  19. 免费下载国内各大音乐平台歌曲
  20. /home/wxl/jdk1.8.0_91/jre/lib/i386/libawt_xawt.so: libXext.so.6: cannot open shared object file: No

热门文章

  1. Tensorflow多GPU计算
  2. 关于嵌入式是前端还是后端
  3. PockerFace 离线版人证比对 SDK 编程指南
  4. 微信小程序中app.js的使用
  5. HTML+CSS详细自学
  6. [CSS]伪类选择器
  7. c语言系统分工 ppt,C语言案例员工管理系统.ppt
  8. 脚本语言有哪些,如JavaScript、Scala、Python
  9. 运动音乐支付多面手,Garmin智能手表新品亮相CES
  10. 前端开发常用命令行工具使用技巧