本节讲深度学习用于文本和序列
用于处理序列的两种基本的深度学习算法分别是循环神经网络 (recurrent neural network) 和一维卷积神经网络(1D convnet)
与其他所有神经网络一样, 深度学习模型不会接收原始文本作为输入, 它只能处理数值张量. 文本向量化 (vectorize) 是指将文本转换为数值张量的过程. 它有多种实现方法
将文本分割为单词, 并将每个单词转换为一个向量
将文本分割为字符, 并将每个字符转换为一个向量
提取单词或字符的 n-gram, 并将每个 n-gram 转换为一个向量. n-gram 是多个连续单词或字符的集合(n-gram 之间可重叠)
将文本分解而成的单元 (单词, 字符或 n-gram) 叫作标记(token), 将文本分解成标记的过程叫作分词(tokenization). 所有文本向量化过程都是应用某种分词方案, 然后将数值向量与生成的标记相关联. 这些向量组合成序列张量, 被输入到深度神经网络中
n-gram 是从一个句子中提取的 N 个 (或更少) 连续单词的集合. 这一概念中的 "单词" 也可以替换为 "字符"
The cat sat on the mat 分解为二元语法 (2-gram) 的集合
{"The", "The cat", "cat", "cat sat", "sat", "sat on", "on", "on the", "the", "the mat", "mat"}
分解为三元语法 (3-gram) 的集合
- {
- "The", "The cat", "cat", "cat sat", "The cat sat",
- "sat", "sat on", "on", "cat sat on", "on the", "the",
- "sat on the", "the mat", "mat", "on the mat"
- }
这样的集合分别叫作二元语法袋 (bag-of-2-grams) 及三元语法袋 (bag-of-3-grams). 这里袋(bag) 这一术语指的是, 我们处理的是标记组成的集合. 这一系列分词方法叫作词袋(bag-of-words). 词袋是一种不保存顺序的分词方法, 因此它往往被用于浅层的语言处理模型, 而不是深度学习模型
将向量与标记相关联的方法
对标记做 one-hot 编码 (one-hot encoding) 与标记嵌入[token embedding, 通常只用于单词, 叫作词嵌入(Word embedding)]
one-hot 编码是将标记转换为向量的最常用, 最基本的方法
它将每个单词与一个唯一的整数索引相关联, 然后将这个整数索引 i 转换为长度为 N 的二进制向量(N 是词表大小), 这个向量只有第 i 个元素是 1, 其余元素都为 0 (也可以进行字符级的 one-hot 编码)
keras one-hot 编码 Demo
- from keras.preprocessing.text import Tokenizer
- samples = ['The cat sat on the mat.', 'The dog ate my homework.']
- # 只考虑前 1000 个最常见的单词
- tokenizer = Tokenizer(num_words=1000)
- # 构建单词索引
- tokenizer.fit_on_texts(samples)
- # 找回单词索引
- word_index = tokenizer.word_index
- print(word_index)
- # 将字符串转换为整数索引组成的列表
- sequences = tokenizer.texts_to_sequences(samples)
- print("转换成的索引序列", sequences)
- text = tokenizer.sequences_to_texts(sequences)
- print("转会的文本", text)
- # 得到 one-hot 二进制表示
- one_hot_results = tokenizer.texts_to_matrix(samples, mode='binary')
- one_num = 0
- for items in one_hot_results:
- for item in items:
- if item == 1:
- one_num += 1
- print("1 的数量为", one_num)
- print(one_hot_results)
结果
one-hot 编码的一种变体是所谓的 one-hot 散列技巧(one-hot hashing trick), 如果词表中唯
一标记的数量太大而无法直接处理, 就可以使用这种技巧
将单词散列编码为固定长度的向量, 通常用一个非常简单的散列函数来实现
这种方法的主要优点在于, 它避免了维护一个显式的单词索引, 从而节省内存并允许数据的在线编码, 缺点就是可能会出现散列冲突
词嵌入
one-hot 编码得到的向量是二进制的, 稀疏的, 维度很高的(维度大小等于词表中的单词个数), 而词嵌入是低维的浮点数向量. 与 one-hot 编码得到的词向量不同, 词嵌入是从数据中学习得到的. 常见的词向量维度是 256,512 或 1024(处理非常大的词表时). 与此相对, onehot 编码的词向量维度通常为 20 000 或更高. 因此, 词向量可以将更多的信息塞入更低的维度中
获取词嵌入有两种方法
在完成主任务 (比如文档分类或情感预测) 的同时学习词嵌入. 在这种情况下, 一开始是随机的词向量, 然后对这些词向量进行学习, 其学习方式与学习神经网络的权重相同
在不同于待解决问题的机器学习任务上预计算好词嵌入, 然后将其加载到模型中. 这些词嵌入叫作预训练词嵌入(pretrained Word embedding)
利用 Embedding 层学习词嵌入
词嵌入的作用应该是将人类的语言映射到几何空间中, 我们希望任意两个词向量之间的几何距离)应该和这两个词的语义距离有关. 可能还希望嵌入空间中的特定方向也是有意义的
Embedding 层的输入是一个二维整数张量, 其形状为 (samples, sequence_length), 它能够嵌入长度可变的序列, 不过一批数据中的所有序列必须具有相同的长度
简单 Demo
- from keras.datasets import imdb
- from keras import preprocessing
- from keras.models import Sequential
- from keras.layers import Flatten, Dense, Embedding
- import matplotlib.pyplot as plt
- max_features = 10000
- maxlen = 20
- (x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features, path='E:\\study\\dataset\\imdb.npz')
- x_train = preprocessing.sequence.pad_sequences(x_train, maxlen=maxlen)
- x_test = preprocessing.sequence.pad_sequences(x_test, 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)
- 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.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()
结果
当可用的训练数据很少, 以至于只用手头数据无法学习适合特定任务的词嵌入, 你可以从预计算的嵌入空间中加载嵌入向量, 而不是在解决问题的同时学习词嵌入. 有许多预计算的词嵌入数据库, 你都可以下载并在 Keras 的 Embedding 层中使用, word2vec 就是其中之一. 另一个常用的是 GloVe(global vectors for Word representation, 词表示全局向量)
没有足够的数据来自己学习真正强大的特征, 但你需要的特征应该是非常通用的, 比如常见的视觉特征或语义特征
新闻情感分类 Demo, 使用 GloVe 预训练词
- import os
- from keras.preprocessing.text import Tokenizer
- from keras.preprocessing.sequence import pad_sequences
- import numpy as np
- from keras.models import Sequential
- from keras.layers import Embedding, Flatten, Dense
- import matplotlib.pyplot as plt
- imdb_dir = 'E:\\study\\dataset\\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)
- # 对 IMDB 原始数据的文本进行分词
- maxlen = 100
- training_samples = 200
- validation_samples = 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
- 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]
- # 解析 GloVe 词嵌入文件
- glove_dir = 'E:\\study\\models\\glove.6B'
- embeddings_index = {}
- f = open(os.path.join(glove_dir, 'glove.6B.100d.txt'))
- 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))
- # 准备 GloVe 词嵌入矩阵(max_words, embedding_dim)
- 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()
- # 将预训练的词嵌入加载到 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.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()
- # 对测试集数据进行分词
- test_dir = os.path.join(imdb_dir, 'test')
- labels = []
- texts = []
- for label_type in ['neg', 'pos']:
- dir_name = os.path.join(test_dir, label_type)
- for fname in sorted(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)
- sequences = tokenizer.texts_to_sequences(texts)
- x_test = pad_sequences(sequences, maxlen=maxlen)
- y_test = np.asarray(labels)
- # 在测试集上评估模型
- model.load_weights('pre_trained_glove_model.h5')
- model.evaluate(x_test, y_test)
模型下的时间太长放弃了, 233
python Deep learning 学习笔记(4)
来源: https://www.cnblogs.com/zhhfan/p/9985991.html