TensorFlow Eager教程全(转)
https://github.com/wizardforcel/data-science-notebook/blob/master/tf/tf-eager-tut
一、如何使用 TensorFlow Eager 构建简单的神经网络
大家好! 在本教程中,我们将使用 TensorFlow 的命令模式构建一个简单的前馈神经网络。 希望你会发现它很有用! 如果你对如何改进代码有任何建议,请告诉我。
教程步骤:
使用的版本:TensorFlow 1.7
第一步:导入有用的库并启用 Eager 模式
# 导入 TensorFlow 和 TensorFlow Eager
import tensorflow as tf
import tensorflow.contrib.eager as tfe # 导入函数来生成玩具分类问题 from sklearn.datasets import make_moons import numpy as np # 导入绘图库 import matplotlib.pyplot as plt %matplotlib inline # 开启 Eager 模式。一旦开启不能撤销!只执行一次。 tfe.enable_eager_execution()
第二步:为二分类生成玩具数据集
我们将生成一个玩具数据集,来训练我们的网络。 我从sklearn
中选择了make_moons
函数。 我相信它对我们的任务来说是完美的,因为类不是线性可分的,因此神经网络将非常有用。
# 为分类生成玩具数据集
# X 是 n_samples x n_features 的矩阵,表示输入特征
# y 是 长度为 n_samples 的向量,表示我们的标签 X, y = make_moons(n_samples=100, noise=0.1, random_state=2018)
第三步:展示生成的数据集
plt.scatter(X[:,0], X[:,1], c=y, cmap=plt.cm.autumn) plt.xlabel('First feature') plt.ylabel('Second feature') plt.title('Toy classification problem') plt.show()
第四步:构建单隐层神经网络(线性 -> ReLU -> 线性输出)
我们的第一个试验是一个简单的神经网络,只有一个隐层。 使用 TensorFlow Eager 构建神经网络模型的最简单方法是使用类。 在初始化期间,你可以定义执行模型正向传播所需的层。
由于这是一个分类问题,我们将使用softmax
交叉熵损失。 通常,我们必须对标签进行单热编码。 为避免这种情况,我们将使用稀疏softmax
损失,它以原始标签作为输入。 无需进一步处理!
class simple_nn(tf.keras.Model):
def __init__(self): super(simple_nn, self).__init__() """ 在这里定义正向传播期间 使用的神经网络层 """ # 隐层 self.dense_layer = tf.layers.Dense(10, activation=tf.nn.relu) # 输出层,无激活函数 self.output_layer = tf.layers.Dense(2, activation=None) def predict(self, input_data): """ 在神经网络上执行正向传播 Args: input_data: 2D tensor of shape (n_samples, n_features). Returns: logits: unnormalized predictions. """ hidden_activations = self.dense_layer(input_data) logits = self.output_layer(hidden_activations) return logits def loss_fn(self, input_data, target): """ 定义训练期间使用的损失函数 """ logits = self.predict(input_data) loss = tf.losses.sparse_softmax_cross_entropy(labels=target, logits=logits) return loss def grads_fn(self, input_data, target): """ 在每个正向步骤中, 动态计算损失值对模型参数的梯度 """ with tfe.GradientTape() as tape: loss = self.loss_fn(input_data, target) return tape.gradient(loss, self.variables) def fit(self, input_data, target, optimizer, num_epochs=500, verbose=50): """ 用于训练模型的函数, 使用所选的优化器,执行所需数量的迭代 """ for i in range(num_epochs): grads = self.grads_fn(input_data, target)
第五步:使用梯度下降训练模型
使用反向传播来训练我们模型的变量。 随意玩玩学习率和迭代数。
X_tensor = tf.constant(X)
y_tensor = tf.constant(y)
optimizer = tf.train.GradientDescentOptimizer(5e-1)
model = simple_nn()
model.fit(X_tensor, y_tensor, optimizer, num_epochs=500, verbose=50) optimizer.apply_gradients(zip(grads, self.variables)) if (i==0) | ((i+1)%verbose==0): print('Loss at epoch %d: %f' %(i+1, self.loss_fn(input_data, target).numpy())) ''' Loss at epoch 1: 0.653288 Loss at epoch 50: 0.283921 Loss at epoch 100: 0.260529 Loss at epoch 150: 0.244092 Loss at epoch 200: 0.221653 Loss at epoch 250: 0.186211 Loss at epoch 300: 0.139418 Loss at epoch 350: 0.103654 Loss at epoch 400: 0.078874 Loss at epoch 450: 0.062550 Loss at epoch 500: 0.051096 '''
第六步:绘制决策边界
用于绘制模型决策边界的代码受到本教程的启发。
# 创建 mesh ,在其中绘制
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01), np.arange(y_min, y_max, 0.01)) # 为每个样本 xx, yy 预测标签 Z = np.argmax(model.predict(tf.constant(np.c_[xx.ravel(), yy.ravel()])).numpy(), axis=1) # 将结果放进彩色绘图 Z = Z.reshape(xx.shape) fig = plt.figure() plt.contourf(xx, yy, Z, cmap=plt.cm.autumn, alpha=0.8) # 绘制我们的训练样本 plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.autumn, edgecolors='k') plt.xlim(xx.min(), xx.max()) plt.ylim(yy.min(), yy.max()) plt.xlabel('First feature', fontsize=15) plt.ylabel('Second feature', fontsize=15) plt.title('Toy classification problem', fontsize=15)
二、在 Eager 模式中使用指标
大家好! 在本教程中,我们将学习如何使用各种指标来评估在 TensorFlow 中使用 Eager 模式时神经网络的表现。
我玩了很久 TensorFlow Eager 模式,我喜欢它。对我来说,与使用声明模式相比,API 看起来非常直观,现在一切看起来都更容易构建。 我现在发现的主要不便之处(我使用的是 1.7 版)是使用 Eager 模式时,tf.metrics
还不兼容。 尽管如此,我已经构建了几个函数,可以帮助你评估网络的表现,同时仍然享受凭空构建网络的强大之处。
教程步骤:
我选择了三个案例:
多分类
对于此任务,我们将使用准确率,混淆矩阵和平均精度以及召回率,来评估我们模型的表现。
不平衡的二分类
当我们处理不平衡的数据集时,模型的准确率不是可靠的度量。 因此,我们将使用 ROC-AUC 分数,这似乎是一个更适合不平衡问题的指标。
回归
为了评估我们的回归模型的性能,我们将使用 R ^ 2 分数(确定系数)。
我相信这些案例的多样性足以帮助你进一步学习任何机器学习项目。 如果你希望我添加下面未遇到的任何额外指标,请告知我们,我会尽力在以后添加它们。 那么,让我们开始吧!
TensorFlow 版本 - 1.7
导入重要的库并开启 Eager 模式
# 导入 TensorFlow 和 TensorFlow Eager
import tensorflow as tf
import tensorflow.contrib.eager as tfe # 导入函数来生成玩具分类问题 from sklearn.datasets import load_wine from sklearn.datasets import make_classification from sklearn.datasets import make_regression # 为数据预处理导入 numpy import numpy as np # 导入绘图库 import matplotlib.pyplot as plt %matplotlib inline # 为降维导入 PCA from sklearn.decomposition import PCA # 开启 Eager 模式。一旦开启不能撤销!只执行一次。 tfe.enable_eager_execution()
第一部分:用于多分类的的数据集
wine_data = load_wine()
print('Type of data in the wine_data dictionary: ', list(wine_data.keys())) ''' Type of data in the wine_data dictionary: ['data', 'target', 'target_names', 'DESCR', 'feature_names'] ''' print('Number of classes: ', len(np.unique(wine_data.target))) # Number of classes: 3 print('Distribution of our targets: ', np.unique(wine_data.target, return_counts=True)[1]) # Distribution of our targets: [59 71 48] print('Number of features in the dataset: ', wine_data.data.shape[1]) # Number of features in the dataset: 13
特征标准化
每个特征的比例变化很大,如下面的单元格所示。 为了加快训练速度,我们将每个特征标准化为零均值和单位标准差。 这个过程称为标准化,它对神经网络的收敛非常有帮助。
# 数据集标准化
wine_data.data = (wine_data.data - np.mean(wine_data.data, axis=0))/np.std(wine_data.data, axis=0) print('Standard deviation of each feature after standardization: ', np.std(wine_data.data, axis=0)) # Standard deviation of each feature after standardization: [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
数据可视化:使用 PCA 降到二维
我们将使用 PCA,仅用于可视化目的。 我们将使用所有 13 个特征来训练我们的神经网络。
让我们看看这三个类如何在 2D 空间中表示。
X_pca = PCA(n_components=2, random_state=2018).fit_transform(wine_data.data) plt.scatter(X_pca[:,0], X_pca[:,1], c=wine_data.target, cmap=plt.cm.spring) plt.xlabel('First PCA component', fontsize=15) plt.ylabel('Second PCA component', fontsize=15) plt.title('Multi-classification problem', fontsize=15) plt.show()
好的,所以这些类看起来很容易分开。 顺便说一句,我实际上在特征标准化之前尝试使用 PCA,粉色和黄色类重叠。 通过在降维之前标准化特征,我们设法在它们之间获得了清晰的界限。
让我们使用 TensorFlow Eager API 构建双层神经网络
你可能已经注意到,使用 TensorFlow Eager 构建模型的最方便方法是使用类。 我认为,为模型使用类可以更容易地组织和添加新组件。 你只需定义初始化期间要使用的层,然后在预测期间使用它们。 它使得在预测阶段更容易阅读模型的架构。
class two_layer_nn(tf.keras.Model):
def __init__(self, output_size=2, loss_type='cross-entropy'): super(two_layer_nn, self).__init__() """ 在这里定义正向传播期间 使用的神经网络层 Args: output_size: int (default=2). loss_type: string, 'cross-entropy' or 'regression' (default='cross-entropy') """ # 第一个隐层 self.dense_1 = tf.layers.Dense(20, activation=tf.nn.relu) # 第二个隐层 self.dense_2 = tf.layers.Dense(10, activation=tf.nn.relu) # 输出层,未缩放的对数概率 self.dense_out = tf.layers.Dense(output_size, activation=None) # 初始化损失类型 self.loss_type = loss_type def predict(self, input_data): """ 在神经网络上执行正向传播 Args: input_data: 2D tensor of shape (n_samples, n_features). Returns: logits: unnormalized predictions. """ layer_1 = self.dense_1(input_data) layer_2 = self.dense_2(layer_1) logits = self.dense_out(layer_2) return logits def loss_fn(self, input_data, target): """ 定义训练期间使用的损失函数 """ preds = self.predict(input_data) if self.loss_type=='cross-entropy': loss = tf.losses.sparse_softmax_cross_entropy(labels=target, logits=preds) else: loss = tf.losses.mean_squared_error(target, preds) return loss def grads_fn(self, input_data, target): """ 在每个正向步骤中, 动态计算损失值对模型参数的梯度 """ with tfe.GradientTape() as tape: loss = self.loss_fn(input_data, target) return tape.gradient(loss, self.variables) def fit(self, input_data, target, optimizer, num_epochs=500, verbose=50, track_accuracy=True): """ 用于训练模型的函数, 使用所选的优化器,执行所需数量的迭代 """ if track_accuracy: # Initialize list to store the accuracy of the model self.hist_accuracy = [] # Initialize class to compute the accuracy metric accuracy = tfe.metrics.Accuracy() for i in range(num_epochs): # Take a step of gradient descent grads = self.grads_fn(input_data, target) optimizer.apply_gradients(zip(grads, self.variables)) if track_accuracy: # Predict targets after taking a step of gradient descent logits = self.predict(X) preds = tf.argmax(logits, axis=1) # Compute the accuracy accuracy(preds, target) # Get the actual result and add it to our list self.hist_accuracy.append(accuracy.result()) # Reset accuracy value (we don't want to track the running mean accuracy) accuracy.init_variables()
准确率指标
为了使用准确率指标评估模型的表现,我们将使用tfe.metrics.Accuracy
类。 在批量训练模型时,此指标非常有用,因为它会在每次调用时计算批量的平均精度。 当我们在每个步骤中使用整个数据集训练模型时,我们将重置此指标,因为我们不希望它跟踪运行中的平均值。
# 创建输入特征和标签。将数据从 numpy 转换为张量
X = tf.constant(wine_data.data)
y = tf.constant(wine_data.target)
# 定义优化器 optimizer = tf.train.GradientDescentOptimizer(5e-1) # 初始化模型 model = two_layer_nn(output_size=3) # 在这里选择迭代数量 num_epochs = 5 # 使用梯度下降训练模型 model.fit(X, y, optimizer, num_epochs=num_epochs) plt.plot(range(num_epochs), model.hist_accuracy); plt.xlabel('Epoch number', fontsize=15); plt.ylabel('Accuracy', fontsize=15); plt.title('Training accuracy history', fontsize=15);
混淆矩阵
在训练完算法后展示混淆矩阵是一种很好的方式,可以全面了解网络表现。 TensorFlow 具有内置函数来计算混淆矩阵,幸运的是它与 Eager 模式兼容。 因此,让我们可视化此数据集的混淆矩阵。
# 获得整个数据集上的预测
logits = model.predict(X)
preds = tf.argmax(logits, axis=1) # 打印混淆矩阵 conf_matrix = tf.confusion_matrix(y, preds, num_classes=3) print('Confusion matrix: \n', conf_matrix.numpy()) ''' Confusion matrix: [[56 3 0] [ 2 66 3] [ 0 1 47]] '''
对角矩阵显示真正例,而矩阵的其它地方显示假正例。
精准率得分
上面计算的混淆矩阵使得计算平均精确率非常容易。 我将在下面实现一个函数,它会自动为你计算。 你还可以指定每个类的权重。 例如,由于某些原因,第二类的精确率可能对你来说更重要。
def precision(labels, predictions, weights=None): conf_matrix = tf.confusion_matrix(labels, predictions, num_classes=3) tp_and_fp = tf.reduce_sum(conf_matrix, axis=0) tp = tf.diag_part(conf_matrix) precision_scores = tp/(tp_and_fp) if weights: precision_score = tf.multiply(precision_scores, weights)/tf.reduce_sum(weights) else: precision_score = tf.reduce_mean(precision_scores) return precision_score precision_score = precision(y, preds, weights=None) print('Average precision: ', precision_score.numpy()) # Average precision: 0.9494581280788177
召回率得分
平均召回率的计算与精确率非常相似。 我们不是对列进行求和,而是对行进行求和,来获得真正例和假负例的总数。
def recall(labels, predictions, weights=None): conf_matrix = tf.confusion_matrix(labels, predictions, num_classes=3) tp_and_fn = tf.reduce_sum(conf_matrix, axis=1) tp = tf.diag_part(conf_matrix) recall_scores = tp/(tp_and_fn) if weights: recall_score = tf.multiply(recall_scores, weights)/tf.reduce_sum(weights) else: recall_score = tf.reduce_mean(recall_scores) return recall_score recall_score = recall(y, preds, weights=None) print('Average precision: ', recall_score.numpy()) # Average precision: 0.9526322246094269
第二部分:不平衡二分类
当你开始使用真实数据集时,你会很快发现大多数问题都是不平衡的。 例如,考虑到异常样本与正常样本的比例,异常检测问题严重不平衡。 在这些情况下,评估网络性能的更合适的指标是 ROC-AUC 得分。 那么,让我们构建我们的不平衡数据集并开始研究它!
XX,, yy == make_classificationmake_cla (n_samples=1000, n_features=2, n_informative=2, n_redundant=0, n_classes=2, n_clusters_per_class=1, flip_y=0.1, class_sep=4, hypercube=False, shift=0.0, scale=1.0, random_state=2018) # 减少标签为 1 的样本数 X = np.vstack([X[y==0], X[y==1][:50]]) y = np.hstack([y[y==0], y[y==1][:50]])
我们将使用相同的神经网络架构。 我们只需用num_classes = 2
初始化模型,因为我们正在处理二分类问题。
# Numpy 数组变为张量
X = tf.constant(X)
y = tf.constant(y)
让我们将模型只训练几个迭代,来避免过拟合。
# 定义优化器
optimizer = tf.train.GradientDescentOptimizer(5e-1)
# 初始化模型 model = two_layer_nn(output_size=2) # 在这里选择迭代数量 num_epochs = 5 # 使用梯度下降训练模型 model.fit(X, y, optimizer, num_epochs=num_epochs)
如何计算 ROC-AUC 得分
为了计算 ROC-AUC 得分,我们将使用tf.metric.auc
的相同方法。 对于每个概率阈值,我们将计算真正例,真负例,假正例和假负例的数量。 在计算这些统计数据后,我们可以计算每个概率阈值的真正例率和真负例率。
为了近似 ROC 曲线下的面积,我们将使用黎曼和和梯形规则。 如果你想了解更多信息,请点击此处。
ROC-AUC 函数
def roc_auc(labels, predictions, thresholds, get_fpr_tpr=True): tpr = [] fpr = [] for th in thresholds: # 计算真正例数量 tp_cases = tf.where((tf.greater_equal(predictions, th)) & (tf.equal(labels, 1))) tp = tf.size(tp_cases) # 计算真负例数量 tn_cases = tf.where((tf.less(predictions, th)) & (tf.equal(labels, 0))) tn = tf.size(tn_cases) # 计算假正例数量 fp_cases = tf.where((tf.greater_equal(predictions, th)) & (tf.equal(labels,0))) fp = tf.size(fp_cases) # 计算假负例数量 fn_cases = tf.where((tf.less(predictions, th)) & (tf.equal(labels,1))) fn = tf.size(fn_cases) # 计算该阈值的真正例率 tpr_th = tp/(tp + fn) # 计算该阈值的假正例率 fpr_th = fp/(fp + tn) # 附加到整个真正例率列表 tpr.append(tpr_th) # 附加到整个假正例率列表 fpr.append(fpr_th) # 使用黎曼和和梯形法则,计算曲线下的近似面积 auc_score = 0 for i in range(0, len(thresholds)-1): height_step = tf.abs(fpr[i+1]-fpr[i]) b1 = tpr[i] b2 = tpr[i+1] step_area = height_step*(b1+b2)/2 auc_score += step_area return auc_score, fpr, tpr
为我们训练的模型计算 ROC-AUC 得分并绘制 ROC 曲线
# 阈值更多意味着曲线下的近似面积的粒度更高
# 随意尝试阈值的数量
num_thresholds = 1000 thresholds = tf.lin_space(0.0, 1.0, num_thresholds).numpy() # 将Softmax应用于我们的预测,因为模型的输出是非标准化的 # 选择我们的正类的预测(样本较少的类) preds = tf.nn.softmax(model.predict(X))[:,1] # 计算 ROC-AUC 得分并获得每个阈值的 TPR 和 FPR auc_score, fpr_list, tpr_list = roc_auc(y, preds, thresholds) print('ROC-AUC score of the model: ', auc_score.numpy()) # ROC-AUC score of the model: 0.93493986 plt.plot(fpr_list, tpr_list, label='AUC score: %.2f' %auc_score); plt.xlabel('False Positive Rate', fontsize=15); plt.ylabel('True Positive Rate', fontsize=15); plt.title('ROC curve'); plt.legend(fontsize=15);
第三部分:用于回归的数据集
我们最终的数据集为简单的回归任务而创建。 在前两个问题中,网络的输出表示样本所属的类。这里网络的输出是连续的,是一个实数。
我们的输入数据集仅包含一个特征,以便使绘图保持简单。 标签y
是实数向量。
让我们创建我们的玩具数据集!
X, y = make_regression(n_samples=100, n_features=1, n_informative=1, noise=30, random_state=2018)
展示输入特征和标签
为了更好地了解我们正在处理的问题,让我们绘制标签和输入特征。
pltplt..scatterscatter((XX,, yy););
pltplt..xlabelxlabel(('Input''Input',, fontsizefontsize=15); plt.ylabel('Target', fontsize=15); plt.title('Toy regression problem', fontsize=15);
# Numpy 数组转为张量
X = tf.constant(X)
y = tf.constant(y)
y = tf.reshape(y, [-1,1]) # 从行向量变为列向量
用于回归任务的神经网络
我们可以重复使用上面创建的双层神经网络。 由于我们只需要预测一个实数,因此网络的输出大小为 1。
我们必须重新定义我们的损失函数,因为我们无法继续使用softmax
交叉熵损失。 相反,我们将使用均方误差损失函数。 我们还将定义一个新的优化器,其学习速率比前一个更小。
随意调整迭代的数量。
# 定义优化器
optimizer = tf.train.GradientDescentOptimizer(1e-4)
# 初始化模型 model = two_layer_nn(output_size=1, loss_type='regression') # 选择迭代数量 num_epochs = 300 # 使用梯度下降训练模型 model.fit(X, y, optimizer, num_epochs=num_epochs, track_accuracy=False)
计算 R^2 得分(决定系数)
如果你曾经处理过回归问题,那么你可能已经听说过这个得分。
这个指标计算输入特征与目标之间的变异百分率,由我们的模型解释。R^2 得分的值范围介于 0 和 1 之间。R^2 得分为 1 意味着该模型可以进行完美的预测。 始终预测目标y
的平均值,R^2 得分为 0。
R^2 可能为的负值。 在这种情况下,这意味着比起总是预测目标变量的平均值的模型,我们的模型做出更糟糕的预测。
由于此度量标准在 TensorFlow 1.5 中不易获得,因此在 Eager 模式下运行时,我在下面的单元格中为它创建了一个小函数。
# 计算 R^2 得分
def r2(labels, predictions): mean_labels = tf.reduce_mean(labels) total_sum_squares = tf.reduce_sum((labels-mean_labels)**2) residual_sum_squares = tf.reduce_sum((labels-predictions)**2) r2_score = 1 - residual_sum_squares/total_sum_squares return r2_score preds = model.predict(X) r2_score = r2(y, preds) print('R2 score: ', r2_score.numpy()) # R2 score: 0.8249999999348803
展示最佳拟合直线
为了可视化我们的神经网络的最佳拟合直线,我们简单地选取X_min
和X_max
之间的线性空间。
# 创建 X_min 和 X_max 之间的数据点来显示最佳拟合直线
X_best_fit = np.arange(X.numpy().min(), X.numpy().max(), 0.001)[:,None]
# X_best_fit 的预测 preds_best_fit = model.predict(X_best_fit) plt.scatter(X.numpy(), y.numpy()); # 原始数据点 plt.plot(X_best_fit, preds_best_fit.numpy(), color='k', linewidth=6, label='$R^2$ score: %.2f' %r2_score) # Our predictions plt.xlabel('Input', fontsize=15); plt.ylabel('Target', fontsize=15); plt.title('Toy regression problem', fontsize=15); plt.legend(fontsize=15);
三、如何保存和恢复训练模型
滚动浏览reddit.com/r/learnmachinelearning
的帖子后,我意识到机器学习项目的主要瓶颈,出现于数据输入流水线和模型的最后阶段,你必须保存模型和 对新数据做出预测。 所以我认为制作一个简单直接的教程,向你展示如何保存和恢复使用 Tensorflow Eager 构建的模型会很有用。
教程的流程图
导入有用的库
# 导入 TensorFlow 和 TensorFlow Eager
import tensorflow as tf
import tensorflow.contrib.eager as tfe # 导入函数来生成玩具分类问题 from sklearn.datasets import make_moons # 开启 Eager 模式。一旦开启不能撤销!只执行一次。 tfe.enable_eager_execution()
第一部分:为二分类构建简单的神经网络
class simple_nn(tf.keras.Model):
def __init__(self): super(simple_nn, self).__init__() """ 在这里定义正向传播期间 使用的神经网络层 """ # 隐层 self.dense_layer = tf.layers.Dense(10, activation=tf.nn.relu) # 输出层,无激活 self.output_layer = tf.layers.Dense(2, activation=None) def predict(self, input_data): """ 在神经网络上执行正向传播 Args: input_data: 2D tensor of shape (n_samples, n_features). Returns: logits: unnormalized predictions. """ hidden_activations = self.dense_layer(input_data) logits = self.output_layer(hidden_activations) return logits def loss_fn(self, input_data, target): """ 定义训练期间使用的损失函数 """ logits = self.predict(input_data) loss = tf.losses.sparse_softmax_cross_entropy(labels=target, logits=logits) return loss def grads_fn(self, input_data, target): """ 在每个正向步骤中, 动态计算损失值对模型参数的梯度 """ with tfe.GradientTape() as tape: loss = self.loss_fn(input_data, target) return tape.gradient(loss, self.variables) def fit(self, input_data, target, optimizer, num_epochs=500, verbose=50): """ 用于训练模型的函数, 使用所选的优化器,执行所需数量的迭代 """ for i in range(num_epochs): grads = self.grads_fn(input_data, target) optimizer.apply_gradients(zip(grads, self.variables)) if (i==0) | ((i+1)%verbose==0): print('Loss at epoch %d: %f' %(i+1, self.loss_fn(input_data, target).numpy()))
第二部分:训练模型
# 为分类生成玩具数据集
# X 是 n_samples x n_features 的矩阵,表示输入特征
# y 是 长度为 n_samples 的向量,表示我们的标签 X, y = make_moons(n_samples=100, noise=0.1, random_state=2018) X_train, y_train = tf.constant(X[:80,:]), tf.constant(y[:80]) X_test, y_test = tf.constant(X[80:,:]), tf.constant(y[80:]) optimizer = tf.train.GradientDescentOptimizer(5e-1) model = simple_nn() model.fit(X_train, y_train, optimizer, num_epochs=500, verbose=50) ''' Loss at epoch 1: 0.658276 Loss at epoch 50: 0.302146 Loss at epoch 100: 0.268594 Loss at epoch 150: 0.247425 Loss at epoch 200: 0.229143 Loss at epoch 250: 0.197839 Loss at epoch 300: 0.143365 Loss at epoch 350: 0.098039 Loss at epoch 400: 0.070781 Loss at epoch 450: 0.053753 Loss at epoch 500: 0.042401 '''
第三部分:保存训练模型
# 指定检查点目录
checkpoint_directory = 'models_checkpoints/SimpleNN/' # 创建模型检查点 checkpoint = tfe.Checkpoint(optimizer=optimizer, model=model, optimizer_step=tf.train.get_or_create_global_step()) # 保存训练模型 checkpoint.save(file_prefix=checkpoint_directory) # 'models_checkpoints/SimpleNN/-1'
第四部分:恢复训练模型
# 重新初始化模型实例
model = simple_nn()
optimizer = tf.train.GradientDescentOptimizer(5e-1)
# 指定检查点目录 checkpoint_directory = 'models_checkpoints/SimpleNN/' # 创建模型检查点 checkpoint = tfe.Checkpoint(optimizer=optimizer, model=model, optimizer_step=tf.train.get_or_create_global_step()) # 从最近的检查点恢复模型 checkpoint.restore(tf.train.latest_checkpoint(checkpoint_directory)) # <tensorflow.contrib.eager.python.checkpointable_utils.CheckpointLoadStatus at 0x7fcfd47d2048>
第五部分:检查模型是否正确恢复
model.fit(X_train, y_train, optimizer, num_epochs=1)
# Loss at epoch 1: 0.042220
损失似乎与我们在之前训练的最后一个迭代中获得的损失一致!
第六部分:对新数据做预测
logits_test = model.predict(X_test)
print(logits_test)
'''
tf.Tensor(
[[ 1.54352813 -0.83117302]
[-1.60523365 2.82397487] [ 2.87589525 -1.36463485] [-1.39461001 2.62404279] [ 0.82305161 -0.55651397] [ 3.53674391 -2.55593046] [-2.97344627 3.46589599] [-1.69372442 2.95660466] [-1.43226137 2.65357974] [ 3.11479995 -1.31765645] [-0.65841567 1.60468631] [-2.27454367 3.60553595] [-1.50170912 2.74410115] [ 0.76261479 -0.44574208] [ 2.34516959 -1.6859307 ] [ 1.92181942 -1.63766352] [ 4.06047684 -3.03988941] [ 1.00252324 -0.78900484] [ 2.79802993 -2.2139734 ] [-1.43933035 2.68037059]], shape=(20, 2), dtype=float64) '''
四、文本序列到 TFRecords
大家好! 在本教程中,我将向你展示如何将原始文本数据解析为 TFRecords。 我知道很多人都卡在输入处理流水线,尤其是当你开始着手自己的个人项目时。 所以我真的希望它对你们任何人都有用!
教程的流程图
虚拟的IMDB文本数据
在实践中,我从斯坦福大学提供的大型电影评论数据集中选择了一些数据样本。
在这里导入有用的库
from nltk.tokenize import word_tokenize
import tensorflow as tf
import pandas as pd import pickle import random import glob import nltk import re try: nltk.data.find('tokenizers/punkt') except LookupError: nltk.download('punkt')
将数据解析为 TFRecords
def imdb2tfrecords(path_data='datasets/dummy_text/', min_word_frequency=5, max_words_review=700): ''' 这个脚本处理数据 并将其保存为默认的 TensorFlow 文件格式:tfrecords。 Args: path_data: the path where the imdb data is stored. min_word_frequency: the minimum frequency of a word, to keep it in the vocabulary. max_words_review: the maximum number of words allowed in a review. ''' # 获取正面/负面评论的文件名 pos_files = glob.glob(path_data + 'pos/*') neg_files = glob.glob(path_data + 'neg/*') # 连接正负评论的文件名 filenames = pos_files + neg_files # 列出数据集中的所有评论 reviews = [open(filenames[i],'r').read() for i in range(len(filenames))] # 移除 HTML 标签 reviews = [re.sub(r'<[^>]+>', ' ', review) for review in reviews] # 将每个评论分词 reviews = [word_tokenize(review) for review in reviews] # 计算每个评论的的长度 len_reviews = [len(review) for review in reviews] # 展开嵌套列表 reviews = [word for review in reviews for word in review] # 计算每个单词的频率 word_frequency = pd.value_counts(reviews) # 仅仅保留频率高于最小值的单词 vocabulary = word_frequency[word_frequency>=min_word_frequency].index.tolist() # 添加未知,起始和终止记号 extra_tokens = ['Unknown_token', 'End_token'] vocabulary += extra_tokens # 创建 word2idx 词典 word2idx = {vocabulary[i]: i for i in range(len(vocabulary))} # 将单词的词汇表写到磁盘 pickle.dump(word2idx, open(path_data + 'word2idx.pkl', 'wb')) def text2tfrecords(filenames, writer, vocabulary, word2idx, max_words_review): ''' 用于将每个评论解析为部分,并作为 tfrecord 写入磁盘的函数。 Args: filenames: the paths of the review files. writer: the writer object for tfrecords. vocabulary: list with all the words included in the vocabulary. word2idx: dictionary of words and their corresponding indexes. ''' # 打乱 filenames random.shuffle(filenames) for filename in filenames: review = open(filename, 'r').read() review = re.sub(r'<[^>]+>', ' ', review) review = word_tokenize(review) # 将 review 归约为最大单词 review = review[-max_words_review:] # 将单词替换为来自 word2idx 的等效索引 review = [word2idx[word] if word in vocabulary else word2idx['Unknown_token'] for word in review] indexed_review = review + [word2idx['End_token']] sequence_length = len(indexed_review) target = 1 if filename.split('/')[-2]=='pos' else 0 # Create a Sequence Example to store our data in ex = tf.train.SequenceExample() # 向我们的示例添加非顺序特性 ex.context.feature['sequence_length'].int64_list.value.append(sequence_length) ex.context.feature['target'].int64_list.value.append(target) # 添加顺序特征 token_indexes = ex.feature_lists.feature_list['token_indexes'] for token_index in indexed_review: token_indexes.feature.add().int64_list.value.append(token_index) writer.write(ex.SerializeToString()) ########################################################################## # Write data to tfrecords.This might take a while. ########################################################################## writer = tf.python_io.TFRecordWriter(path_data + 'dummy.tfrecords') text2tfrecords(filenames, writer, vocabulary, word2idx, max_words_review) imdb2tfrecords(path_data='datasets/dummy_text/')
将 TFRecords 解析为 TF 张量
def parse_imdb_sequence(record):
'''
解析 imdb tfrecords 的脚本
Returns: token_indexes: sequence of token indexes present in the review. target: the target of the movie review. sequence_length: the length of the sequence. ''' context_features = { 'sequence_length': tf.FixedLenFeature([], dtype=tf.int64), 'target': tf.FixedLenFeature([], dtype=tf.int64), } sequence_features = { 'token_indexes': tf.FixedLenSequenceFeature([], dtype=tf.int64), } context_parsed, sequence_parsed = tf.parse_single_sequence_example(record, context_features=context_features, sequence_features=sequence_features) return (sequence_parsed['token_indexes'], context_parsed['target'], context_parsed['sequence_length'])
如果你希望我在本教程中添加任何内容,请告诉我,我将很乐意进一步改善它。
五、如何将原始图片数据转换为 TFRecords
大家好! 与前一个教程一样,本教程的重点是自动化数据输入流水线。
大多数情况下,我们的数据集太大而无法读取到内存,因此我们必须准备一个流水线,用于从硬盘批量读取数据。 我总是将我的原始数据(文本,图像,表格)处理为 TFRecords,因为它让我的生活变得更加容易。
教程的流程图
本教程将包含以下部分:
- 创建一个函数,读取原始图像并将其转换为 TFRecords 的。
- 创建一个函数,将 TFRecords 解析为 TF 张量。
所以废话不多说,让我们开始吧。
导入有用的库
import tensorflow as tf
import tensorflow.contrib.eager as tfe
import glob
# 开启 Eager 模式。一旦开启不能撤销!只执行一次。 tfe.enable_eager_execution()
将原始数据转换为 TFRecords
对于此任务,我们将使用 FER2013 数据集中的一些图像,你可以在datasets/dummy_images
文件夹中找到这些图像。 情感标签可以在图像的文件名中找到。 例如,图片id7_3.jpg
情感标签为 3,其对应于状态'Happy'
(快乐),如下面的字典中所示。
# 获取每个情感的下标的含义
emotion_cat = {0:'Angry', 1:'Disgust', 2:'Fear', 3:'Happy', 4:'Sad', 5:'Surprise', 6:'Neutral'} def img2tfrecords(path_data='datasets/dummy_images/', image_format='jpeg'): ''' 用于将原始图像以及它们标签转换为 TFRecords 的函数 辅助函数的原始的源代码:https://goo.gl/jEhp2B Args: path_data: the location of the raw images image_format: the format of the raw images (e.g. 'png', 'jpeg') ''' def _int64_feature(value): '''辅助函数''' return tf.train.Feature(int64_list=tf.train.Int64List(value=[value])) def _bytes_feature(value): '''辅助函数''' return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) # 获取目录中每个图像的文件名 filenames = glob.glob(path_data + '*' + image_format) # 创建 TFRecordWriter writer = tf.python_io.TFRecordWriter(path_data + 'dummy.tfrecords') # 遍历每个图像,并将其写到 TFrecords 文件中 for filename in filenames: # 读取原始图像 img = tf.read_file(filename).numpy() # 从文件名中解析它的标签 label = int(filename.split('_')[-1].split('.')[0]) # 创建样本(图像,标签) example = tf.train.Example(features=tf.train.Features(feature={ 'label': _int64_feature(label), 'image': _bytes_feature(img)})) # 向 TFRecords 写出序列化样本 writer.write(example.SerializeToString()) # 将原始数据转换为 TFRecords img2tfrecords()
将 TFRecords 解析为 TF 张量
def parser(record):
'''解析 TFRecords 样本的函数''' # 定义你想要解析的特征 features = {'image': tf.FixedLenFeature((), tf.string), 'label': tf.FixedLenFeature((), tf.int64)} # 解析样本 parsed = tf.parse_single_example(record, features) # 解码图像 img = tf.image.decode_image(parsed['image']) return img, parsed['label']
如果你希望我在本教程中添加任何内容,请告诉我,我将很乐意进一步改善它。
六、如何使用 TensorFlow Eager 从 TFRecords 批量读取数据
大家好,本教程再次关注输入流水线。 这很简单,但我记得当我第一次开始批量读取数据时,我陷入了相当多的细节,所以我想我可能会在这里分享我的方法。 我真的希望它对你们中的一些人有用。
教程的流程图:
我们将研究两种情况:
- 可变序列长度的输入数据 - 在这种情况下,我们将填充批次到最大序列长度。
- 图像数据
两种情况的数据都存储为 TFRecords。 你可以查看教程的第四和第五章,了解如何将原始数转换为 TFRecords。
那么,让我们直接开始编程!
导入有用的库
# 导入数据可视化库
import matplotlib.pyplot as plt
# 使绘图内嵌在笔记本中 %matplotlib inline # 导入 TensorFlow 和 TensorFlow Eager import tensorflow as tf import tensorflow.contrib.eager as tfe # 开启 Eager 模式。一旦开启不能撤销!只执行一次。 tfe.enable_eager_execution()
第一部分:读取可变序列长度的数据
本教程的第一部分向你介绍如何读取不同长度的输入数据。 在我们的例子中,我们使用了大型电影数据库中的虚拟 IMDB 评论。 你可以想象,每个评论都有不同的单词数。 因此,当我们读取一批数据时,我们将序列填充到批次中的最大序列长度。
为了了解我如何获得单词索引序列,以及标签和序列长度,请参阅第四章。
创建函数来解析每个 TFRecord
def parse_imdb_sequence(record):
'''
用于解析 imdb tfrecords 的脚本
Returns: token_indexes: sequence of token indexes present in the review. target: the target of the movie review. sequence_length: the length of the sequence. ''' context_features = { 'sequence_length': tf.FixedLenFeature([], dtype=tf.int64), 'target': tf.FixedLenFeature([], dtype=tf.int64), } sequence_features = { 'token_indexes': tf.FixedLenSequenceFeature([], dtype=tf.int64), } context_parsed, sequence_parsed = tf.parse_single_sequence_example(record, context_features=context_features, sequence_features=sequence_features) return (sequence_parsed['token_indexes'], context_parsed['target'], context_parsed['sequence_length'])
创建数据集迭代器
正如你在上面的函数中所看到的,在解析每个记录之后,我们返回一系列单词索引,评论标签和序列长度。 在padded_batch
方法中,我们只填充记录的第一个元素:单词索引的序列。 在每个示例中,标签和序列长度不需要填充,因为它们只是单个数字。 因此,padded_shapes
将是:
[None]
-> 将序列填充到最大维度,还不知道,因此是None
。[]
-> 标签没有填充。[]
-> 序列长度没有填充。
# 选取批量大小
batch_size = 2
# 从 TFRecords 创建数据集 dataset = tf.data.TFRecordDataset('datasets/dummy_text/dummy.tfrecords') dataset = dataset.map(parse_imdb_sequence).shuffle(buffer_size=10000) dataset = dataset.padded_batch(batch_size, padded_shapes=([None],[],[]))
遍历数据一次
for review, target, sequence_length in tfe.Iterator(dataset):
print(target)
'''
tf.Tensor([0 1], shape=(2,), dtype=int64)
tf.Tensor([1 0], shape=(2,), dtype=int64) tf.Tensor([0 1], shape=(2,), dtype=int64) ''' for review, target, sequence_length in tfe.Iterator(dataset): print(review.shape) ''' (2, 145) (2, 139) (2, 171) ''' for review, target, sequence_length in tfe.Iterator(dataset): print(sequence_length) ''' tf.Tensor([137 151], shape=(2,), dtype=int64) tf.Tensor([139 171], shape=(2,), dtype=int64) tf.Tensor([145 124], shape=(2,), dtype=int64) '''
第二部分:批量读取图像(以及它们的标签)
在本教程的第二部分中,我们将通过批量读取图像,将存储为 TFRecords 的图像可视化。 这些图像是 FER2013 数据集中的一个小型子样本。
创建函数来解析每个记录并解码图片
def parser(record):
'''
解析 TFRecords 样本的函数
Returns: img: decoded image. label: the corresponding label of the image. ''' # 定义你想要解析的特征 features = {'image': tf.FixedLenFeature((), tf.string), 'label': tf.FixedLenFeature((), tf.int64)} # 解析样本 parsed = tf.parse_single_example(record, features) # 解码图像 img = tf.image.decode_image(parsed['image']) return img, parsed['label']
创建数据集迭代器
# 选取批量大小
batch_size = 5
# 从 TFRecords 创建数据集 dataset = tf.data.TFRecordDataset('datasets/dummy_images/dummy.tfrecords') dataset = dataset.map(parser).shuffle(buffer_size=10000) dataset = dataset.batch(batch_size)
遍历数据集一次。展示图像。
# Dictionary that stores the correspondence between integer labels and the emotions
emotion_cat = {0:'Angry', 1:'Disgust', 2:'Fear', 3:'Happy', 4:'Sad', 5:'Surprise', 6:'Neutral'} # 遍历数据集一次 for image, label in tfe.Iterator(dataset): # 为每个图像批量创建子图 f, axarr = plt.subplots(1, int(image.shape[0]), figsize=(14, 6)) # 绘制图像 for i in range(image.shape[0]): axarr[i].imshow(image[i,:,:,0], cmap='gray') axarr[i].set_title('Emotion: %s' %emotion_cat[label[i].numpy()])
如果你希望我在本教程中添加任何内容,请与我们联系。 我会尽力添加它!
七、使用 TensorFlow Eager 构建用于情感识别的卷积神经网络(CNN)
对于深度学习,我最喜欢的部分之一就是我可以解决一些问题,其中我自己可以测试神经网络。 到目前为止,我建立的最有趣的神经网络是用于情感识别的 CNN。 我已经设法通过网络传递我的网络摄像头视频,并实时预测了我的情绪(使用 GTX-1070)。 相当容易上瘾!
因此,如果你想将工作与乐趣结合起来,那么你一定要仔细阅读本教程。 另外,这是熟悉 Eager API 的好方法!
教程步骤
- 下载并处理 Kaggle 上提供的 FER2013 数据集。
- 整个数据集上的探索性数据分析。
- 将数据集拆分为训练和开发数据集。
- 标准化图像。
- 使用
tf.data.Dataset
API 遍历训练和开发数据集。 - 在 Eager 模式下为 CNN 创建一个类。
- 能够保存模型或从先前的检查点恢复。
- 创建一个损失函数,一个优化器和一个梯度计算函数。
- 用梯度下降训练模型。
- 从头开始或者从预训练模型开始。
- 在训练期间可视化表现并计算准确率。
- 使用集成梯度可视化样本图像上的 CNN 归属。
- 使用 OpenCV 和 Haar 级联算法在新图像上测试 CNN。
导入有用的库
# 导入 TensorFlow 和 TensorFlow Eager
import tensorflow as tf
import tensorflow.contrib.eager as tfe # 导入函数来生成玩具分类问题 from sklearn.datasets import make_moons import numpy as np # 导入绘图库 import matplotlib.pyplot as plt %matplotlib inline # 开启 Eager 模式。一旦开启不能撤销!只执行一次。 tfe.enable_eager_execution()
下载数据集
为了训练我们的 CNN,我们将使用 Kaggle 上提供的 FER2013 数据集。 你必须在他们的平台上自己下载数据集,遗憾的是我无法公开分享数据。 尽管如此,数据集只有 96.4 MB,因此你应该能够立即下载它。 你可以在这里下载。
下载完数据后,将其解压缩并放入名为datasets
的文件夹中,这样你就不必对下面的代码进行任何修改。
好的,让我们开始探索性数据分析!
探索性数据分析
在构建任何机器学习模型之前,建议对数据集进行探索性数据分析。 这使你有机会发现数据集中的任何缺陷,如类之间的强烈不平衡,低质量图像等。
我发现机器学习项目中出现的大多数错误,都是由于数据处理不正确造成的。 如果你在发现模型没有用后才开始调查数据集,那么找到这些错误会更加困难。
所以,我给你的建议是:在构建任何模型之前总是分析数据。
# 读取输入数据。假设已经解压了数据集,并放入名为 data 的文件夹中。
path_data = 'datasets/fer2013/fer2013.csv' data = pd.read_csv(path_data) print('Number of samples in the dataset: ', data.shape[0]) # Number of samples in the dataset: 35887 # 查看前五行 data.head(5)
emotion | pixels | Usage | |
---|---|---|---|
0 | 0 | 70 80 82 72 58 58 60 63 54 58 60 48 89 115 121... | Training |
1 | 0 | 151 150 147 155 148 133 111 140 170 174 182 15... | Training |
2 | 2 | 231 212 156 164 174 138 161 173 182 200 106 38... | Training |
3 | 4 | 24 32 36 30 32 23 19 20 30 41 21 22 32 34 21 1... | Training |
4 | 6 | 4 0 0 0 0 0 0 0 0 0 0 0 3 15 23 28 48 50 58 84... | Training |
# 获取每个表情的含义
emotion_cat = {0:'Angry', 1:'Disgust', 2:'Fear', 3:'Happy', 4:'Sad', 5:'Surprise', 6:'Neutral'} # 查看标签分布(检查不平衡) target_counts = data['emotion'].value_counts().reset_index(drop=False) target_counts.columns = ['emotion', 'number_samples'] target_counts['emotion'] = target_counts['emotion'].map(emotion_cat) target_counts
emotion | number_samples | |
---|---|---|
0 | Happy | 8989 |
1 | Neutral | 6198 |
2 | Sad | 6077 |
3 | Fear | 5121 |
4 | Angry | 4953 |
5 | Surprise | 4002 |
6 | Disgust | 547 |
如你所见,数据集非常不平衡。 特别是对于情绪Disgust
。 这将使这个类的训练更加困难,因为网络将有更少的机会来学习这种表情的表示。
在我们训练网络之后,稍后我们会看到这是否会严重影响我们网络的训练。
我们来看看一些图片!
图像当前表示为整数的字符串,每个整数表示一个像素的强度。 我们将处理字符串。将其表示为整数列表。
# 将图像从字符串换换位整数列表
data['pixels'] = data['pixels'].apply(lambda x: [int(pixel) for pixel in x.split()]) # 修改这里的种子来查看其它图像 random_seed = 2 # 随机选择十个图像 data_sample = data.sample(10, random_state=random_seed) # 为图像创建子图 f, axarr = plt.subplots(2, 5, figsize=(20, 10)) # 绘制图像 i, j = 0, 0 for idx, row in data_sample.iterrows(): img = np.array(row['pixels']).reshape(48,48) axarr[i,j].imshow(img, cmap='gray') axarr[i,j].set_title(emotion_cat[row['emotion']]) if j==4: i += 1 j = 0 else: j += 1
将数据集拆分为训练/开发,并按最大值标准化图像
data_traindata_tra = data[data['Usage']=='Training'] size_train = data_train.shape[0] print('Number samples in the training dataset: ', size_train) data_dev = data[data['Usage']!='Training'] size_dev = data_dev.shape[0] print('Number samples in the development dataset: ', size_dev) ''' Number samples in the training dataset: 28709 Number samples in the development dataset: 7178 ''' # 获取训练输入和标签 X_train, y_train = data_train['pixels'].tolist(), data_train['emotion'].as_matrix() # 将图像形状修改为 4D(样本数,宽,高,通道数) X_train = np.array(X_train, dtype='float32').reshape(-1,48,48,1) # 使用最大值标准化图像(最大像素密度为 255) X_train = X_train/255.0 # 获取开发输入和标签 X_dev, y_dev = data_dev['pixels'].tolist(), data_dev['emotion'].as_matrix() # 将图像形状修改为 4D(样本数,宽,高,通道数) X_dev = np.array(X_dev, dtype='float32').reshape(-1,48,48,1) # 使用最大值标准化图像 X_dev = X_dev/255.0
使用tf.data.Dataset
API
为了准备我们的数据集用作 CNN 的输入,我们将使用tf.data.Dataset
API,将我们刚刚创建的 numpy 数组转换为 TF 张量。 由于此数据集比以前教程中的数据集大得多,因此我们实际上必须将数据批量提供给模型。
通常,为了提高计算效率,你可以选择与内存一样大的批量。 但是,根据我的经验,如果我在训练期间使用较小的批量,我会在测试数据上获得更好的结果。 随意调整批量大小,看看你是否得到了与我相同的结论。
# 随意调整批量大小
# 通常较小的批量大小在测试集上获取更好的结果
batch_size = 64 training_data = tf.data.Dataset.from_tensor_slices((X_train, y_train[:,None])).batch(batch_size) eval_data = tf.data.Dataset.from_tensor_slices((X_dev, y_dev[:,None])).batch(batch_size)
在 Eager 模式下创建 CNN 模型
CNN 架构在下面的单元格中创建。 如你所见,EmotionRecognitionCNN
类继承自tf.keras.Model
类,因为我们想要跟踪包含任何可训练参数的层(例如卷积的权重,批量标准化层的平均值)。 这使我们易于保存这些变量,然后在我们想要继续训练网络时将其恢复。
这个 CNN 的原始架构可以在这里找到(使用 keras 构建)。 我认为如果你开始使用比 ResNet 更简单的架构,那将非常有用。 对于这个网络规模,它的效果非常好。
你可以使用它,添加更多的层,增加层的数量,过滤器等。看看你是否可以获得更好的结果。
有一点可以肯定的是,dropout 越高,网络效果越好。
class EmotionRecognitionCNN(tf.keras.Model):
def __init__(self, num_classes, device='cpu:0', checkpoint_directory=None): ''' 定义在正向传播期间使用的参数化层,你要在它上面运行计算的设备,以及检查点目录。 Args: num_classes: the number of labels in the network. device: string, 'cpu:n' or 'gpu:n' (n can vary). Default, 'cpu:0'. checkpoint_directory: the directory where you would like to save or restore a model. ''' super(EmotionRecognitionCNN, self).__init__() # 初始化层 self.conv1 = tf.layers.Conv2D(16, 5, padding='same', activation=None) self.batch1 = tf.layers.BatchNormalization() self.conv2 = tf.layers.Conv2D(16, 5, 2, padding='same', activation=None) self.batch2 = tf.layers.BatchNormalization() self.conv3 = tf.layers.Conv2D(32, 5, padding='same', activation=None) self.batch3 = tf.layers.BatchNormalization() self.conv4 = tf.layers.Conv2D(32, 5, 2, padding='same', activation=None) self.batch4 = tf.layers.BatchNormalization() self.conv5 = tf.layers.Conv2D(64, 3, padding='same', activation=None) self.batch5 = tf.layers.BatchNormalization() self.conv6 = tf.layers.Conv2D(64, 3, 2, padding='same', activation=None) self.batch6 = tf.layers.BatchNormalization() self.conv7 = tf.layers.Conv2D(64, 1, padding='same', activation=None) self.batch7 = tf.layers.BatchNormalization() self.conv8 = tf.layers.Conv2D(128, 3, 2, padding='same', activation=None) self.batch8 = tf.keras.layers.BatchNormalization() self.conv9 = tf.layers.Conv2D(256, 1, padding='same', activation=None) self.batch9 = tf.keras.layers.BatchNormalization() self.conv10 = tf.layers.Conv2D(128, 3, 2, padding='same', activation=None) self.conv11 = tf.layers.Conv2D(256, 1, padding='same', activation=None) self.batch11 = tf.layers.BatchNormalization() self.conv12 = tf.layers.Conv2D(num_classes, 3, 2, padding='same', activation=None) # 定义设备 self.device = device # 定义检查点目录 self.checkpoint_directory = checkpoint_directory def predict(self, images, training): """ 根据输入样本预测每个类的概率。 Args: images: 4D tensor. Either an image or a batch of images. training: Boolean. Either the network is predicting in training mode or not. """ x = self.conv1(images) x = self.batch1(x, training=training) x = self.conv2(x) x = self.batch2(x, training=training) x = tf.nn.relu(x) x = tf.layers.dropout(x, rate=0.4, training=training) x =