Yukyukuon / blog

博客的文章
1 stars 0 forks source link

Deep Learning with Python 6 #28

Open Yukyukuon opened 1 year ago

Yukyukuon commented 1 year ago

第六章 深度学习用于文本和序列

处理序列的两种基本的深度学习算法分别是循环神经网络(recurrentneural network)和一维卷积神经网络(1D convnet)。

处理文本数据

文本向量化(vectorize)是指将文本转换为数值张量的过程:

将文本分解而成的单元(单词、字符或n-gram)叫作标记(token),将文本分解成标记的过程叫作分词(tokenization)。

将向量与标记相关联的方法有很多种:

one-hot编码

它将每个单词与一个唯一的整数索引相关联,然后将这个整数索引i 转换为长度为N 的二进制向量(N 是词表大小),这个向量只有第i 个元素是1,其余元素都为0。

特点:one-hot 编码得到的向量是二进制的、稀疏的(绝大部分元素都是0)、维度很高的(维度大小等于词表中的单词个数)

单词级的one-hot编码

# Word level one-hot encoding
import numpy as np
samples = ['The cat sat on the mat.', 'The dog  ate my homework.']

token_index = {}
for sample in samples:
    for word in sample.split():  # 根据空格split()将句子拆开
        if word not in token_index:
            token_index[word] = len(token_index) + 1 # 为每个唯一单词指定一个唯一索引。注意,没有为索引编号0 指定单词

max_length = 10
# 开一个都是0的3维张量
results = np.zeros(shape=(len(samples), max_length, max(token_index.values()) + 1))

for i, sample in enumerate(samples):
    # list()[:max_length]截取到max_length之前的元素
    for j, word in list(enumerate(sample.split()))[:max_length]:
        index = token_index.get(word)
        results[i, j, index] = 1.

字符级的one-hot编码

# Character level one-hot encoding  
import string
samples = ['The cat sat on the mat.', 'The dog ate my homework.']

# 所有可打印的ASCII 字符
characters = string.printable
# zip(,)将前后的部分组合起来, dict() 创建映射
token_index = dict(zip(range(1, len(characters) + 1), characters))
max_length = 50
results = np.zeros((len(samples), max_length, max(token_index.keys()) + 1))

for i, samples in enumerate(samples):
    for j, character in enumerate(sample):
        index = token_index.get(character)
        results[i, j, index] = 1.

Keras实现单词级的one-hot编码

from tensorflow.keras.preprocessing.text import Tokenizer
samples = ['The cat sat on the mat.', 'The dog ate my homework.']

# 创建一个分词器(tokenizer),设置为只考虑前1000 个最常见的单词
tokenizer = Tokenizer(num_words=1000)
# 构建单词索引,根据samples创建一个词汇表(tokenizer.word_docs可显示内容)
tokenizer.fit_on_texts(samples)
# 将字符串转换为整数索引组成的列表[[1, 2, 3, 4, 1, 5], [1, 6, 7, 8, 9]]
sequences = tokenizer.texts_to_sequences(samples)
# 也可以直接得到one-hot 二进制表示。arry(2, 1000)
one_hot_results = tokenizer.texts_to_matrix(samples, mode='binary')
# 找回单词索引(变成字典)
word_index = tokenizer.word_index

结果:

{'the': 1,
 'cat': 2,
 'sat': 3,
 'on': 4,
 'mat': 5,
 'dog': 6,
 'ate': 7,
 'my': 8,
 'homework': 9}

使用散列技巧的单词级的one-hot 编码

# 使用hash生成乱数编码
dimensionality = 1000
max_length = 10

results = np.zeros((len(samples), max_length, dimensionality))
for i, sample in enumerate(samples):
    for j, word in list(enumerate(sample.split()))[:max_length]:
        index = abs(hash(word)) % dimensionality
        results[i, j, index] = 1.

Word Embeddings(词嵌入)

定义:利用向量,方向和距离的一些特性,试图去把单词之间的关系描述出来
将单词与向量相关联还有另一种常用的强大方法,就是使用密集的词向量(word vector),也叫词嵌入(word embedding)。 常见的词向量维度是256、512 或1024(处理非常大的词表时)。

特点:低维的浮点数向量(即密集向量,与稀疏向量相对)

获取词嵌入有两种方法:

一个好的词嵌入空间在很大程度上取决于你的任务,每个新任务都学习一个新的嵌入空间。 反向传播让这种学习变得很简单,我们要做的就是学习一个层的权重,这个层就是Embedding 层。

利用Embedding 层学习词嵌入

from keras.layers import Embedding
# Embedding 层至少需要两个参数:
# 标记的个数(这里是1000,即最大单词索引+1)和嵌入的维度(这里是64)
embedding_layer = Embedding(1000, 64)

まとめ:从原始文本到词嵌入模版

将句子嵌入到向量序列中,然后将其展平,最后在上面训练一个Dense 层。但此处将使用预训练的词嵌入。

import os 

# 处理IMDB 原始数据的标签
imdb_dir = '../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), encoding = 'utf8')
            texts.append(f.read())
            f.close()
            if label_type == 'neg':
                labels.append(0)
            else:
                labels.append(1)

对数据进行分词

from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
import numpy as np

maxlen = 100
training_samples = 200
validation_samples = 1000
max_words = 1000

tokerizer = 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))

把刚刚的sequences变为矩阵

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 = '../data/glove/'

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))

构建一个可以加载到Embedding 层中的嵌入矩阵。它必须是一个形状为(max_words, embedding_dim) 的矩阵,对于单词索引(在分词时构建)中索引为i 的单词,这个矩阵的元素i 就是这个单词对应的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)
        # 嵌入索引(embeddings_index)中找不到的词,其嵌入向量全为0
        if embedding_vector is not None:
            embedding_matrix[i] = embedding_vector

定义模型

from keras.models import Sequential
from keras.layers import Embedding, Flatten, Dense

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 嵌入

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('../model/modpre_trained_glove_model.h5')

绘制结果

import matplotlib.pyplot as plt

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()