Keras 功能 API 与深度学习模型应用
1. Keras 功能 API 简介
Keras 功能 API 将每个层定义为一个函数,并提供运算符将这些函数组合成一个更大的计算图。在这个体系中,函数是一种具有单一输入和单一输出的变换。例如,函数
y = f(x)
定义了一个输入为
x
、输出为
y
的函数
f
。
以下是一个简单的 Keras 顺序模型示例:
from keras.models import Sequential
from keras.layers.core import dense, Activation
model = Sequential([
dense(32, input_dim=784),
Activation("sigmoid"),
dense(10),
Activation("softmax"),
])
model.compile(loss="categorical_crossentropy", optimizer="adam")
顺序模型将网络表示为层的线性管道或列表。而使用 Keras 功能 API 可以将网络重新定义为嵌套函数的组合:
from keras.layers import Input
from keras.layers.core import dense
from keras.models import Model
from keras.layers.core import Activation
inputs = Input(shape=(784,))
x = dense(32)(inputs)
x = Activation("sigmoid")(x)
x = dense(10)(x)
predictions = Activation("softmax")(x)
model = Model(inputs=inputs, outputs=predictions)
model.compile(loss="categorical_crossentropy", optimizer="adam")
由于模型是层(也是函数)的组合,因此模型本身也是一个函数。可以将训练好的模型当作另一个层,通过对形状合适的输入张量调用它来使用。例如,使用 Keras 的
TimeDistributed
包装器可以扩展一个图像分类模型以处理图像序列:
sequence_predictions = TimeDistributed(model)(input_sequences)
功能 API 可以定义任何可以用顺序 API 定义的网络,此外,以下类型的网络只能用功能 API 定义:
- 具有多个输入和输出的模型
- 由多个子模型组成的模型
- 使用共享层的模型
具有多个输入和输出的模型通过分别组合输入和输出,然后在
Model
构造函数的输入和输出参数中传入输入函数数组和输出函数数组来定义:
model = Model(inputs=[input1, input2], outputs=[output1, output2])
这类模型通常由多个子网络组成,其计算结果会合并到最终结果中。
merge
函数提供了多种合并中间结果的方法,如向量加法、点积和连接。
共享层在功能 API 中也很有用。共享层只需定义一次,并在需要共享其权重的每个管道中引用。
2. 回归网络
监督学习的两种主要技术是分类和回归。在这两种情况下,模型都使用数据进行训练以预测已知标签。在分类中,这些标签是离散值,如图像类别;在回归中,标签是连续值,如股票价格。
分类模型通常在末尾有一个具有非线性激活的密集层,其输出维度对应于模型可以预测的类别数量。而回归模型的末尾也有一个密集层,但只有一个输出,即输出维度为 1,且没有非线性激活。回归模型常用的损失函数是均方误差(MSE),不过也可以使用 Keras 目标页面列出的其他目标。
3. Keras 回归示例:预测空气中的苯含量
我们将使用 UCI 机器学习库中的空气质量数据集来预测大气中苯的浓度。该数据集包含 9358 个每小时平均读数的实例,记录时间为 2004 年 3 月至 2005 年 2 月。
操作步骤如下:
1.
导入必要的库
:
from keras.layers import Input
from keras.layers.core import dense
from keras.models import Model
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd
- 加载和预处理数据 :
DATA_DIR = "../data"
AIRQUALITY_FILE = os.path.join(DATA_DIR, "AirQualityUCI.csv")
aqdf = pd.read_csv(AIRQUALITY_FILE, sep=";", decimal=",", header=0)
# 删除第一列和最后两列
del aqdf["Date"]
del aqdf["Time"]
del aqdf["Unnamed: 15"]
del aqdf["Unnamed: 16"]
# 用每列的均值填充 NaN
aqdf = aqdf.fillna(aqdf.mean())
Xorig = aqdf.as_matrix()
- 数据缩放 :
scaler = StandardScaler()
Xscaled = scaler.fit_transform(Xorig)
# 保存均值和标准差
Xmeans = scaler.mean_
Xstds = scaler.scale_
y = Xscaled[:, 3]
X = np.delete(Xscaled, 3, axis=1)
- 划分训练集和测试集 :
train_size = int(0.7 * X.shape[0])
Xtrain, Xtest, ytrain, ytest = X[0:train_size], X[train_size:], y[0:train_size], y[train_size:]
- 定义网络 :
readings = Input(shape=(12,))
x = dense(8, activation="relu", kernel_initializer="glorot_uniform")(readings)
benzene = dense(1, kernel_initializer="glorot_uniform")(x)
model = Model(inputs=[readings], outputs=[benzene])
model.compile(loss="mse", optimizer="adam")
- 训练模型 :
NUM_EPOCHS = 20
BATCH_SIZE = 10
history = model.fit(Xtrain, ytrain, batch_size=BATCH_SIZE, epochs=NUM_EPOCHS, validation_split=0.2)
- 评估模型 :
ytest_ = model.predict(Xtest).flatten()
for i in range(10):
label = (ytest[i] * Xstds[3]) + Xmeans[3]
prediction = (ytest_[i] * Xstds[3]) + Xmeans[3]
print("Benzene Conc. expected: {:.3f}, predicted: {:.3f}".format(label, prediction))
- 绘制结果图 :
plt.plot(np.arange(ytest.shape[0]), (ytest * Xstds[3]) / Xmeans[3], color="b", label="actual")
plt.plot(np.arange(ytest_.shape[0]), (ytest_ * Xstds[3]) / Xmeans[3], color="r", alpha=0.5, label="predicted")
plt.xlabel("time")
plt.ylabel("C6H6 concentrations")
plt.legend(loc="best")
plt.show()
4. 无监督学习:自编码器
自编码器是一类神经网络,它试图使用反向传播将输入重新创建为目标。自编码器由两部分组成:编码器和解码器。编码器读取输入并将其压缩为紧凑表示,解码器读取紧凑表示并从中重新创建输入。自编码器通过最小化重建误差来学习恒等函数。
自编码器中隐藏单元的数量通常少于输入(和输出)单元的数量,这迫使编码器学习输入的压缩表示。如果输入数据中存在特征之间的相关性,自编码器将发现这些相关性,并最终学习到类似于主成分分析(PCA)的低维数据表示。
训练好自编码器后,通常会丢弃解码器组件,使用编码器组件生成输入的紧凑表示。或者,可以将编码器用作特征检测器,通过在隐藏层连接一个 softmax 分类器来构建分类器。
自编码器的编码器和解码器组件可以使用密集、卷积或循环网络实现,具体取决于要建模的数据类型。例如,密集网络适用于构建协同过滤模型的自编码器;卷积神经网络适用于去除人脸眼镜的用例;循环网络适用于处理文本数据的自编码器。
自编码器还可以堆叠,通过依次堆叠将输入压缩为越来越小表示的编码器,以及按相反顺序堆叠解码器。堆叠自编码器具有更强的表达能力,其连续的表示层可以捕获输入的层次分组。
5. Keras 自编码器示例:生成句子向量
我们将构建并训练一个基于 LSTM 的自编码器,为路透社 - 21578 语料库中的文档生成句子向量。
操作步骤如下:
1.
导入必要的库
:
from sklearn.model_selection import train_test_split
from keras.callbacks import ModelCheckpoint
from keras.layers import Input
from keras.layers.core import RepeatVector
from keras.layers.recurrent import LSTM
from keras.layers.wrappers import Bidirectional
from keras.models import Model
from keras.preprocessing import sequence
from scipy.stats import describe
import collections
import matplotlib.pyplot as plt
import nltk
import numpy as np
import os
- 处理数据 :
sents = []
fsent = open(sent_filename, "rb")
for line in fsent:
docid, sent_id, sent = line.strip().split("t")
sents.append(sent)
fsent.close()
- 构建词汇表 :
def is_number(n):
temp = re.sub("[.,-/]", "", n)
return temp.isdigit()
word_freqs = collections.Counter()
sent_lens = []
parsed_sentences = []
for sent in sentences:
words = nltk.word_tokenize(sent)
parsed_words = []
for word in words:
if is_number(word):
word = "9"
word_freqs[word.lower()] += 1
parsed_words.append(word)
sent_lens.append(len(words))
parsed_sentences.append(" ".join(parsed_words))
- 设置常量 :
VOCAB_SIZE = 5000
SEQUENCE_LEN = 50
- 构建查找表 :
word2id = {}
word2id["PAD"] = 0
word2id["UNK"] = 1
for v, (k, _) in enumerate(word_freqs.most_common(VOCAB_SIZE - 2)):
word2id[k] = v + 2
id2word = {v:k for k, v in word2id.items()}
- 加载 GloVe 嵌入 :
EMBED_SIZE = 50
def lookup_word2id(word):
try:
return word2id[word]
except KeyError:
return word2id["UNK"]
def load_glove_vectors(glove_file, word2id, embed_size):
embedding = np.zeros((len(word2id), embed_size))
fglove = open(glove_file, "rb")
for line in fglove:
cols = line.strip().split()
word = cols[0]
if embed_size == 0:
embed_size = len(cols) - 1
if word2id.has_key(word):
vec = np.array([float(v) for v in cols[1:]])
embedding[lookup_word2id(word)] = vec
embedding[word2id["PAD"]] = np.zeros((embed_size))
embedding[word2id["UNK"]] = np.random.uniform(-1, 1, embed_size)
return embedding
embeddings = load_glove_vectors(os.path.join(
DATA_DIR, "glove.6B.{:d}d.txt".format(EMBED_SIZE)), word2id, EMBED_SIZE)
- 定义数据生成器 :
BATCH_SIZE = 64
def sentence_generator(X, embeddings, batch_size):
while True:
num_recs = X.shape[0]
indices = np.random.permutation(np.arange(num_recs))
num_batches = num_recs // batch_size
for bid in range(num_batches):
sids = indices[bid * batch_size : (bid + 1) * batch_size]
Xbatch = embeddings[X[sids, :]]
yield Xbatch, Xbatch
train_size = 0.7
Xtrain, Xtest = train_test_split(sent_wids, train_size=train_size)
train_gen = sentence_generator(Xtrain, embeddings, BATCH_SIZE)
test_gen = sentence_generator(Xtest, embeddings, BATCH_SIZE)
- 定义自编码器 :
inputs = Input(shape=(SEQUENCE_LEN, EMBED_SIZE), name="input")
encoded = Bidirectional(LSTM(LATENT_SIZE), merge_mode="sum", name="encoder_lstm")(inputs)
decoded = RepeatVector(SEQUENCE_LEN, name="repeater")(encoded)
decoded = Bidirectional(LSTM(EMBED_SIZE, return_sequences=True), merge_mode="sum", name="decoder_lstm")(decoded)
autoencoder = Model(inputs, decoded)
autoencoder.compile(optimizer="sgd", loss="mse")
- 训练自编码器 :
num_train_steps = len(Xtrain) // BATCH_SIZE
num_test_steps = len(Xtest) // BATCH_SIZE
checkpoint = ModelCheckpoint(filepath=os.path.join(DATA_DIR, "sent-thoughts-autoencoder.h5"), save_best_only=True)
history = autoencoder.fit_generator(train_gen, steps_per_epoch=num_train_steps, epochs=NUM_EPOCHS, validation_data=test_gen, validation_steps=num_test_steps, callbacks=[checkpoint])
- 评估自编码器 :
encoder = Model(autoencoder.input, autoencoder.get_layer("encoder_lstm").output)
def compute_cosine_similarity(x, y):
return np.dot(x, y) / (np.linalg.norm(x, 2) * np.linalg.norm(y, 2))
k = 500
cosims = np.zeros((k))
i = 0
for bid in range(num_test_steps):
xtest, ytest = test_gen.next()
ytest_ = autoencoder.predict(xtest)
Xvec = encoder.predict(xtest)
Yvec = encoder.predict(ytest_)
for rid in range(Xvec.shape[0]):
if i >= k:
break
cosims[i] = compute_cosine_similarity(Xvec[rid], Yvec[rid])
if i <= 10:
print(cosims[i])
i += 1
if i >= k:
break
通过以上步骤,我们可以利用 Keras 功能 API 构建回归模型和自编码器,并在实际数据上进行训练和评估。这些模型在不同的任务中都有广泛的应用,为解决各种机器学习问题提供了有效的工具。
Keras 功能 API 与深度学习模型应用(续)
6. 自编码器训练结果分析
在完成自编码器的训练后,我们需要对其性能进行评估。由于自编码器的输出是词嵌入矩阵,而词汇是离散的,并非每个输出嵌入都对应一个单词。因此,我们采用比较编码器产生的潜在向量的方法来评估自编码器。
首先,我们将编码器组件提取为一个独立的网络:
encoder = Model(autoencoder.input, autoencoder.get_layer("encoder_lstm").output)
然后,我们在测试集上运行自编码器以得到预测的嵌入。接着,将输入嵌入和预测嵌入分别通过编码器,生成句子向量,并使用余弦相似度比较这两个向量。余弦相似度接近 1 表示高度相似,接近 0 表示相似度低。
以下是具体的评估代码:
def compute_cosine_similarity(x, y):
return np.dot(x, y) / (np.linalg.norm(x, 2) * np.linalg.norm(y, 2))
k = 500
cosims = np.zeros((k))
i = 0
for bid in range(num_test_steps):
xtest, ytest = test_gen.next()
ytest_ = autoencoder.predict(xtest)
Xvec = encoder.predict(xtest)
Yvec = encoder.predict(ytest_)
for rid in range(Xvec.shape[0]):
if i >= k:
break
cosims[i] = compute_cosine_similarity(Xvec[rid], Yvec[rid])
if i <= 10:
print(cosims[i])
i += 1
if i >= k:
break
运行上述代码对 500 个测试句子的随机子集进行评估,得到的前 10 个余弦相似度值如下:
| 序号 | 余弦相似度值 |
| ---- | ---- |
| 1 | 0.982818722725 |
| 2 | 0.970908224583 |
| 3 | 0.98131018877 |
| 4 | 0.974798440933 |
| 5 | 0.968060493469 |
| 6 | 0.976065933704 |
| 7 | 0.96712064743 |
| 8 | 0.949920475483 |
| 9 | 0.973583400249 |
| 10 | 0.980291545391 |
从这些值可以看出,由源嵌入和自编码器产生的相应目标嵌入生成的句子向量非常相似,这表明自编码器能够生成良好的潜在表示。
7. 总结与应用拓展
通过前面的介绍,我们了解了 Keras 功能 API 的强大之处,它不仅可以定义简单的顺序模型,还能处理具有多个输入和输出、共享层等复杂结构的模型。同时,我们还学习了回归网络和自编码器的原理及实现方法。
以下是一个简单的流程图,展示了使用 Keras 功能 API 构建模型的一般流程:
graph LR
A[导入必要的库] --> B[准备数据]
B --> C[定义模型结构]
C --> D[编译模型]
D --> E[训练模型]
E --> F[评估模型]
F --> G[应用模型]
在实际应用中,Keras 功能 API 可以应用于各种领域:
-
图像识别
:可以构建具有多个输入(如图像和元数据)和输出(如分类和定位)的模型,提高图像识别的准确性。
-
自然语言处理
:在处理文本数据时,使用共享层可以更好地捕捉文本的语义信息,同时自编码器可以用于生成文本的潜在表示,为文本分类、情感分析等任务提供支持。
-
时间序列分析
:对于时间序列数据,如股票价格预测、气象数据预测等,回归模型可以根据历史数据预测未来的连续值。
总之,Keras 功能 API 为深度学习模型的构建和应用提供了灵活且高效的解决方案。通过不断学习和实践,我们可以利用这些技术解决更多复杂的实际问题。
通过本文的介绍,你可以掌握 Keras 功能 API 的基本使用方法,构建回归模型和自编码器,并对模型进行训练和评估。希望这些知识能帮助你在深度学习领域取得更好的成果。
超级会员免费看
9万+

被折叠的 条评论
为什么被折叠?



