同本文一块儿发布的另一篇文章中,提到了 BlueDot 公司,这个公司致力于利用人工智能保护全球人民免受传染病的侵害,在本次疫情尚未引发强烈关注时,就提早一周发出预警,一周的时间,多么宝贵!python
他们的 AI 预警系统,就用到了深度学习对文本的处理,这个系统抓取网络上大量的新闻、公开声明等获取到的数十万的信息,对天然语言进行处理,咱们今天就聊聊深度学习如何对文本的简单处理。正则表达式
文本,String 或 Text,就是字符的序列或单词的序列,最多见的是单词的处理(咱们暂时不考虑中文,中文的理解和处理与英文相比要复杂得多)。计算机就是固化的数学,对文本的处理,在本质上来讲就是固化的统计学,这样采用统计学处理后的模型就能够解决许多简单的问题了。下面咱们开始吧。网络
与以前一致,若是原始要训练的数据不是向量,咱们要进行向量化,文本的向量化,有几种方式:app
我喜欢吃火……,你猜我接下来会说的是什么?1-gram 接下来讲什么均可以,这个词与前文不要紧;2-gram 接下来可能说“把,柴,焰”等,组成词语“火把、火柴、火焰”;3-gram 接下来可能说“锅”,组成“吃火锅”,这个几率更大一些。先简单这么理解,n-gram 就是与前 n-1 个词有关。dom
咱们今天先来填以前挖下来的一个坑,当时说之后将介绍 one-hot,如今是时候了。学习
def one_hot(): samples = ['The cat sat on the mat', 'The dog ate my homework'] token_index = {} # 分割成单词 for sample in samples: for word in sample.split(): if word not in token_index: token_index[word] = len(token_index) + 1 # {'The': 1, 'cat': 2, 'sat': 3, 'on': 4, 'the': 5, 'mat.': 6, 'dog': 7, 'ate': 8, 'my': 9, 'homework.': 10} print(token_index) max_length = 8 results = np.zeros(shape=(len(samples), max_length, max(token_index.values()) + 1)) for i, sample in enumerate(samples): for j, word in list(enumerate(sample.split()))[:max_length]: index = token_index.get(word) results[i, j, index] = 1. print(results)
咱们看到,这个数据是很差的,mat 和 homework 后面都分别跟了一个英文的句话 '.',要炫技写那种高级的正则表达式去匹配这个莫名其妙的符号吗?固然不是了,没错,Keras 有内置的方法。编码
def keras_one_hot(): samples = ['The cat sat on the mat.', 'The dog ate my homework.'] tokenizer = Tokenizer(num_words=1000) tokenizer.fit_on_texts(samples) sequences = tokenizer.texts_to_sequences(samples) print(sequences) one_hot_results = tokenizer.texts_to_matrix(samples, mode='binary') print(one_hot_results) word_index = tokenizer.word_index print(word_index) print('Found %s unique tokens.' % len(word_index))
这里的 num_words 和上面的 max_length 都是用来表示多少个最经常使用的单词,控制好这个,能够大大的减小运算量训练时间,甚至有点时候能更好的提升准确率,但愿引发必定注意。咱们还能够看到获得的编码的向量,很大一部分都是 0,不够紧凑,这会致使大量的内存占用,很差很差,有什么什么其余办法呢?答案是确定的。人工智能
也叫词向量。词嵌入一般是密集的,维度低的(25六、5十二、1024)。那到底什么叫词嵌入呢?spa
本文咱们的主题是处理文本信息,文本信息就是有语义的,对于没有语义的文本咱们什么也干不了,可是咱们以前的处理方法,其实就是几率上的统计,,是一种单纯的计算,没有理解的含义(或者说不多),可是考虑到真实状况,“很是好” 和 “很是棒” 的含义是相近的,它们与 “很是差” 的含义是相反的,所以咱们但愿转换成向量的时候,前两个向量距离小,与后一个向量距离大。所以看下面一张图,是否是就很容易理解了呢:3d
可能直接让你去实现这个功能有点难,幸亏 Keras 简化了这个问题,Embedding 是内置的网络层,能够完成这个映射关系。如今理解这个概念后,咱们再来看看 IMDB 问题(电影评论情感预测),代码就简单了,差不均可以达到 75%的准确率:
def imdb_run(): max_features = 10000 maxlen = 20 (x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features) x_train = preprocessing.sequence.pad_sequences(x_train, maxlen=maxlen) model = Sequential() model.add(Embedding(10000, 8, input_length=maxlen)) model.add(Flatten()) model.add(Dense(1, activation='sigmoid')) model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc']) model.summary() history = model.fit(x_train, y_train, epochs=10, batch_size=32, validation_split=0.2)
咱们的数据量有点少,怎么办呢?上一节咱们在处理图像的时候,用到的方法是使用预训练的网络,这里咱们采用相似的方法,采用预训练的词嵌入。最流行的两种词嵌入是 GloVe 和 Word2Vec,咱们后面仍是会在合适的时候分别介绍这两个词嵌入。今天咱们采用 GloVe 的方法,具体作法我写在了代码的注释中。咱们仍是先看结果,代码仍是放在最后:
很快就过拟合了,你可能以为这个验证精度接近 60%,考虑到训练样本只有 200 个,这个结果然的还挺不错的,固然,你可能不信,那么我再给出两组对比图,一组是没有词嵌入的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uem4R2hO-1583414769394)(https://upload-images.jianshu...]
验证精度明显偏低,再给出 2000 个训练集的数据:
这个精度就高了不少,追求这个高低不是咱们的目的,咱们的目的是说明词嵌入是有效的,咱们达到了这个目的,好了,接下来咱们看看代码吧:
#!/usr/bin/env python3 import os import time import matplotlib.pyplot as plt import numpy as np from keras.layers import Embedding, Flatten, Dense from keras.models import Sequential from keras.preprocessing.sequence import pad_sequences from keras.preprocessing.text import Tokenizer def deal(): # http://mng.bz/0tIo imdb_dir = '/Users/renyuzhuo/Documents/PycharmProjects/Data/aclImdb' train_dir = os.path.join(imdb_dir, 'train') labels = [] texts = [] # 读出全部数据 for label_type in ['neg', 'pos']: dir_name = os.path.join(train_dir, label_type) for fname in os.listdir(dir_name): if fname[-4:] == '.txt': f = open(os.path.join(dir_name, fname)) texts.append(f.read()) f.close() if label_type == 'neg': labels.append(0) else: labels.append(1) # 对全部数据进行分词 # 每一个评论最多 100 个单词 maxlen = 100 # 训练样本数量 training_samples = 200 # 验证样本数量 validation_samples = 10000 # 只取最多见 10000 个单词 max_words = 10000 # 分词,前文已经介绍过了 tokenizer = Tokenizer(num_words=max_words) tokenizer.fit_on_texts(texts) sequences = tokenizer.texts_to_sequences(texts) word_index = tokenizer.word_index print('Found %s unique tokens.' % len(word_index)) # 将整数列表转换成张量 data = pad_sequences(sequences, maxlen=maxlen) labels = np.asarray(labels) print('Shape of data tensor:', data.shape) print('Shape of label tensor:', labels.shape) # 打乱数据 indices = np.arange(data.shape[0]) np.random.shuffle(indices) data = data[indices] labels = labels[indices] # 切割成训练集和验证集 x_train = data[:training_samples] y_train = labels[:training_samples] x_val = data[training_samples: training_samples + validation_samples] y_val = labels[training_samples: training_samples + validation_samples] # 下载词嵌入数据,下载地址:https: // nlp.stanford.edu / projects / glove glove_dir = '/Users/renyuzhuo/Documents/PycharmProjects/Data/glove.6B' embeddings_index = {} f = open(os.path.join(glove_dir, 'glove.6B.100d.txt')) # 构建单词与其x向量表示的索引 for line in f: values = line.split() word = values[0] coefs = np.asarray(values[1:], dtype='float32') embeddings_index[word] = coefs f.close() print('Found %s word vectors.' % len(embeddings_index)) # 构建嵌入矩阵 embedding_dim = 100 embedding_matrix = np.zeros((max_words, embedding_dim)) for word, i in word_index.items(): if i < max_words: embedding_vector = embeddings_index.get(word) if embedding_vector is not None: embedding_matrix[i] = embedding_vector # 构建模型 model = Sequential() model.add(Embedding(max_words, embedding_dim, input_length=maxlen)) model.add(Flatten()) model.add(Dense(32, activation='relu')) model.add(Dense(1, activation='sigmoid')) model.summary() # 将 GloVe 加载到 Embedding 层,且将其设置为不可训练 model.layers[0].set_weights([embedding_matrix]) model.layers[0].trainable = False # 训练模型 model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc']) history = model.fit(x_train, y_train, epochs=10, batch_size=32, validation_data=(x_val, y_val)) model.save_weights('pre_trained_glove_model.h5') # 画图 acc = history.history['acc'] val_acc = history.history['val_acc'] loss = history.history['loss'] val_loss = history.history['val_loss'] epochs = range(1, len(acc) + 1) plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b', label='Validation acc') plt.title('Training and validation accuracy') plt.legend() plt.show() plt.figure() plt.plot(epochs, loss, 'bo', label='Training loss') plt.plot(epochs, val_loss, 'b', label='Validation loss') plt.title('Training and validation loss') plt.legend() plt.show() if __name__ == "__main__": time_start = time.time() deal() time_end = time.time() print('Time Used: ', time_end - time_start)