你会怎么定义机器学习?
机器学习是一门能够让系统从数据中学习的计算机科学。
最常见的两个监督式任务是回归和分类。
常见的无监督式任务包括聚类、可视化、降维和关联规则学习。
在线学习系统可以进行增量学习,与批量学习系统正好相反。这使得它能够快速适应不断变化的数据和自动化系统,并且能够在大量的数据上进行训练。
核外算法可以处理计算机主内存无法应对的大量数据。它将数据分割成小批量,然后使用在线学习技术从这些小批量中学习。
模型参数与学习算法的超参数之间有什么区别?
模型有一个或多个参数,这些参数决定了模型对新的给定实例会做出怎样的预测(比如,线性模型的斜率)。学习算法试图找到这些参数的最佳值,使得该模型能够很好的泛化至新的实例。超参数是学习算法本身的参数,不是模型的参数(比如,要应用的正则化数量)。
基于模型的学习算法搜索是什么?它们最常使用的策略是什么?它们如何做出预测?
基于模型的学习算法搜索使模型泛化最佳的模型参数值。通常通过使成本函数最小化来训练这样的系统,成本函数衡量的是系统对训练数据的预测有多坏,如果模型有正则化,则再加上一个对模型复杂度的惩罚。学习算法最后找到的参数值就是最终得到的预测函数,只需要将实例特征提供给这个预测函数即可进行预测。
机器学习面临的一些主要挑战是:数据缺乏、数据质量差、数据不具代表性、特征不具信息量、模型过于简单对训练数据拟合不足、以及模型过于复杂对训练数据过拟合。
如果你的模型在训练数据上表现好,但是应用到新的实例上的泛化结果却很糟糕,是怎么回事?能提出三种可能的解决方案吗?
该模型很可能过度拟合训练数据(或者在训练数据上运气太好)。
可能的解决方案是:获取更多数据,简化模型(选择更简单的算法、减少使用的参数或特征数量、对模型正则化),或者是减少训练数据中的噪声。
验证集的目的是什么?
验证集用来比较不同模型。它可以用来选择最佳模型和调整超参数。
如果使用测试集调整超参数会出现什么问题?
如果使用测试集来调整超参数,会有过度拟合测试集的风险,最后测量的泛化误差会过于乐观(最后启动的模型性能比预期的要差)。
什么是交叉验证?它为什么比验证集更好?
通过交叉验证技术,可以不需要单独的验证集实现模型比较(用于模型选择和调整超参数)。者节省了宝贵的训练数据。
回归问题的性能指标
df.where详解
分层抽样详解
检验属性之间的相关性
缺失值处理
处理文本和分类属性
特征缩放
用numpy连接两个矩阵
np.c_
和np.r_
的用法解析交叉验证
MNIST数据报错:[WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。
参考链接:scikit-learn使用fetch_mldata无法下载MNIST数据集问题解决方法
StratifiedKFold
cross_val_predict
confusion_matrix
decision_function()
该方法返回每个实例的分数,然后就可以根据这些分数,使用任意阈值进行预测。
使用cross_val_predict()函数获取训练集中所有实例的分数
y_scores = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3, method="decision_function")
有了这些分数,可以使用precision_recall_curve()函数来计算所有可能的阈值的精度和召回率
precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_scores)
def plot_precision_recall_vs_threshold(precisions, recalls, thresholds):
plt.plot(thresholds, precisions[:-1], "b--", label="Precision")
plt.plot(thresholds, recalls[:-1], "g-", label="Recall")
plt.xlabel("threshold")
plt.legend(loc = "upper left")
plt.ylim([0, 1])
plot_precision_recall_vs_threshold(precisions, recalls, thresholds)
plt.show()
roc_curve()
受试者工作特征曲线
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(y_train_5, y_scores)
def plot_roc_curve(fpr, tpr, label=None):
plt.plot(fpr, tpr, linewidth=2, label=label)
plt.plot([0, 1], [0, 1], 'k--')
plt.axis([0, 1, 0, 1])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plot_roc_curve(fpr, tpr)
plt.show()
roc_auc_score()
ROC和PR曲线的选择
多类别分类器
sklearn可以检测到你尝试使用二元分类算法进行多类别分类任务,它会自动运行OvR(SVM分类器除外,它会使用OvO)。
如果想要强制sklearn使用一对一或者一对多策略,可以使用OneVsOneClassifier或OneVsRestClassifier类。
from sklearn.multiclass import OneVsOneClassifier
ovo_clf = OneVsOneClassifier(SGDClassifier(random_state=42))
ovo_clf.fit(X_train, y_train)
ovo_clf.predict([some_digit])
len(ovo_clf.estimators_)
错误分析:cross_val_predict() + confusion_matrix()
线性回归算法比较
算法 | m很大 | 是否支持核外 | n很大 | 超参数 | 是否需要缩放 | sklearn |
---|---|---|---|---|---|---|
标准方程 | 快 | 否 | 慢 | 0 | 否 | LinearRegression |
批量梯度下降 | 慢 | 否 | 快 | 2 | 是 | n/a |
随机梯度下降 | 快 | 是 | 快 | ≥2 | 是 | SGDRegressor |
小批量梯度下降 | 快 | 是 | 快 | ≥2 | 是 | n/a |
其中,m是训练实例的数量,n是特征数量。
多项式特征
偏差/方差权衡
线性回归
练习部分摘抄
如果你的训练集里特征的数值大小迥异,什么算法可能会受到影响?受影响程度如何?你应该怎么做?
如果训练集的特征数值具有非常迥异的尺寸比例,成本函数将呈现为细长的碗状,这导致梯度下降算法将耗费很长时间来收敛。要解决这个问题,需要在训练模型之前先对数据进行缩放。值得注意的是,使用标准方程法,不经过特征缩放也能正常工作。
训练逻辑回归模型时,梯度下降是否会困于局部最小值?
不会,因为它的成本函数是凸函数。
假设你使用的是批量梯度下降,并且每一轮训练都绘制出其验证误差,如果发现验证误差持续上升,可能发生了什么?你如何解决这个问题?
可能性之一是学习率太高,算法开始发散所致。如果训练误差也开始上升,那么很明显你要降低学习率了。但是,如果训练误差没有上升,那么模型可能过度拟合训练集,应该立即停止训练。
哪种梯度下降算法能最快达到最优解的附近?那种会收敛?如何使其他算法同样收敛?
随机梯度下降的训练迭代最快,因为它一次只考虑一个训练实例,所以通常来说,它会最快到达全局最优的附近(或者是批量非常小的小批量梯度下降)。但是,只有批量梯度下降才会经过足够长时间的训练后真正收敛。对于随机梯度下降和小批量梯度下降来说,除非逐渐调低学习率,否则将一直围绕最小值上上下下。
核函数
用于SVM分类的sklearn类的比较
类 | 时间复杂度 | 是否支持核外 | 是否需要缩放 | 核技巧 |
---|---|---|---|---|
LinearSVC | O(mxn) | 否 | 是 | 否 |
SGDClassifier | O(mxn) | 是 | 是 | 否 |
SVC | O( m 2 m^2 m2xn)-O( m 3 m^3 m3xn) | 否 | 是 | 是 |
练习摘抄
使用SVM时,对输入值进行缩放为什么重要?
支持向量机拟合类别之间可能的/最宽的街道,所以如果训练集不经过缩放,SVM将趋于忽略值较小的特征。
SVM分类器在对实例进行分类时,会输出信心分数吗?概率呢?
支持向量机分类器能够输出测试实例与决策边界之间的距离,你可以将其作为信心分数。但是这个分数不能直接转化为类别概率的估算。如果创建SVM时,在sklearn中设置probability=True,那么训练完后,算法将使用逻辑回归对SVM分数进行校准(对训练数据额外进行5折交叉验证的训练),从而得到概率值。这会给SVM添加predict_proba()核predict_log_proba()两种方法。
如果训练集有上千万个实例核几百个特征,你应该使用SVM原始问题还是对偶问题来训练模型呢?
这个问题仅适用于线性支持向量机,因为核SVM只能使用对偶问题。对于SVM问题来说,原始形式的计算复杂度与训练实例的数量成正比,而其对偶形式的计算复杂度与某个介于 m 2 m^2 m2和 m 3 m^3 m3之间的数量成正比。所以如果实例的数量以百万计,一定要使用原始问题,因为对偶问题会非常慢。
决策树的可视化
生成一个决策树模型tree_clf
导出成dot格式的文件
export_graphviz(
tree_clf,
out_file=("./iris_tree.dot"),
feature_names=iris.feature_names[2:],
class_names=iris.target_names,
rounded=True,
filled=True
)
dot转png
dot -Tpng iris_tree.dot -o iris_tree.png
决策树的优势
基尼不纯度还是信息熵
正则化超参数
min_*
或是减小max_*
将使模型正则化不稳定性
习题摘抄
如果训练集有100万个实例,训练决策树(无约束)大致的深度是多少?
一个包含m个叶节点的均衡二叉树的深度等于 l o g 2 m log_2m log2m的四舍五入。通常来说,二元决策树训练到最后大体都是平衡的。如果不加以限制,最后平均每个叶节点一个实例。因此,如果训练集包含一百万个实例,那么决策树深度约等于 l o g 2 ( 1 0 6 ) ≈ 20 log_2(10^6)\approx 20 log2(106)≈20层。实际上,会更多一些,因为决策树通常不可能完美平衡。
通常来说,子节点的基尼不纯度是高于还是低于父节点?是通常更高/更低?还是永远更高/更低?
一个节点的基尼不纯度通常比其父节点低。这是通过CART训练算法的成本函数确保的。该算法分裂每个节点的方法,是使其子节点的基尼不纯度的加权之和最小。但是,如果一个子节点的不纯度远小于另一个,那么也有可能使子节点的基尼不纯度比其父节点高,只要那个不纯度更低的子节点能够抵偿这个增加即可。
举例:
假设一个节点包含4个A类别的实例和1个B类别的实例,其基尼不纯度等于 1 − 1 5 2 − 4 5 2 = 0.32 1-\frac15^2-\frac45^2=0.32 1−512−542=0.32。现在我们假设数据使一维的,并且实例的排列顺序如下:A,B,A,A,A。你可以验证,算法将在第二个实例后拆分该节点,从而生成两个子节点所包含的实例分别为A,B和A,A,A。第一个子节点的基尼不纯度为 1 − 1 2 2 − 1 2 2 = 0.5 1-\frac12^2-\frac12^2=0.5 1−212−212=0.5,比其父节点要高。这是因为第二个子节点是纯的,所以总的加权基尼不纯度等于 2 5 ∗ 0.5 + 3 5 ∗ 0.5 = 0.2 \frac25*0.5+\frac35*0.5=0.2 52∗0.5+53∗0.5=0.2低于父节点的基尼不纯度。
如果在包含100万个实例的训练集上训练决策树需要一个小时,那么在包含1000万个实例的训练集上训练决策树,大概需要多长时间?
决策树的训练复杂度为 O ( n ∗ m l o g ( m ) ) O(n*mlog(m)) O(n∗mlog(m))。所以,如果将训练集大小乘以10,训练时间将乘以
K = ( n × 10 m × l o g ( 10 m ) ) / ( n × m × l o g ( m ) ) = 10 × l o g ( 10 m ) / l o g ( m ) K=(n \times 10m \times log(10m)) / (n \times m \times log(m))=10 \times log(10m) / log(m) K=(n×10m×log(10m))/(n×m×log(m))=10×log(10m)/log(m)
如果 m = 1 0 6 m=10^6 m=106,那么 K ≈ 11.7 K \approx 11.7 K≈11.7,所以训练1000万个实例大约需要11.7小时。
如果训练集包含100000个实例,设置presort=True可以加快训练么?
只有当数据集小于数千个实例时,预处理训练集才可以加速训练。如果包含100000个实例,设置presort=True会显著减慢训练。
scipy.stats.mode
投票分类器
使用不同的训练方法训练同样的数据集。
from sklearn.ensemble import VotingClassifier:
voting_clf = VotingClassifier(
estimators=[
('lr', log_clf),
('rf', rnd_clf),
('svc', svm_clf)
],
voting='hard'
)
voting_clf.fit(X_train, y_train)
bagging和pasting
每个预测器使用的算法相同,但是在不同的训练集随机子集上进行训练。
采样时样本放回:bagging(boostrap aggregating,自举汇聚法),统计学中,放回重新采样称为自助法(bootstrapping)。
采样时样本不放回:pasting。
from sklearn.ensemble import BaggingClassifier
bag_clf = BaggingClassifier(
DecisionTreeClassifier(), n_estimators=500, max_samples=100, bootstrap=True, n_jobs=-1
)
bag_clf.fit(X_train, y_train)
如果想使用pasting,只需要设置bootstrap=False
集成预测的泛化效果很可能比单独的分类器要好一些:二者偏差相近,但是集成的方差更小。(两边训练集上的错误数量差不多,但是集成的决策边界更规则)
由于自助法给每个预测器的训练子集引入了更高的多样性,所以最后bagging比pasting的偏差略高,但这也意味着预测器之间的关联度更低,所以集成的方差降低。
总之,bagging生成的模型通常更好。
包外评估
对于任意给定的预测器,使用bagging,有些实例可能会被采样多次,而有些实例则可能根本不被采样。
BaggingClassifier默认采样m个训练实例,然后放回样本(bootstrap=True),m是训练集的大小。这意味着对于每个预测器来说,平均只对63%的训练实例进行采样。(随着m增长,这个比率接近 1 − e x p ( − 1 ) ≈ 63.212 1-exp(-1)\approx63.212% 1−exp(−1)≈63.212)。剩余37%未被采样的训练实例成为**包外(oob)**实例。注意,对所有预测其来说,这是不一样的37%。
同个设置oob_score=True自动进行包外评估,通过变量oob_score_可以得到最终的评估分数。
bag_clf = BaggingClassifier(
DecisionTreeClassifier(), n_estimators=500, bootstrap=True, n_jobs=-1, oob_score=True
)
bag_clf.fit(X_train, y_train)
bag_clf.oob_score_
每个训练实例的包外决策函数也可以通过变量oob_decision_function_获得。
bag_clf.oob_decision_function_
Random Patches和随机子空间
max_features
和bootstrap_features
。它们的工作方式跟max_samples和bootstrap相同,只是抽样对象不再是实例,而是特征。因此,每个预测器将用输入特征的随机子集进行训练。极端随机树(Extra-Trees)
特征重要性
提升法(boosting)
AdaBoost(自适应提升法)
新预测器对其前序进行纠正的办法之一,就是更多的关注前序拟合不足的训练实例。从而使新的预测器不断地越来越专注于难缠的问题,这就是AdaBoost使用的技术。
例如,要构建一个AdaBoost分类器,首先需要训练一个基础分类器(比如决策树),用它对训练集进行预测。然后对错误分类的训练实例增加其相对权重,接着,使用这个最新的权重对第二个分类器进行训练,然后再次对训练集进行预测,继续更新权重,并不断循环向前。
AdaBoost这种依序循环的学习技术跟梯度下降有一些异曲同工之处,差别只在于——不再是调整单个预测器的参数使成本函数最小化,而是不断在集成中加入预测器,使模型越来越好。
这种依序学习技术有一个重要的缺陷就是无法并行,因为每个预测器只能在前一个预测器训练完成并评估之后才能开始训练。因此,在扩展方面,它的表现不如bagging和pasting方法。
循环过程:计算新预测器的权重,更新实例权重,然后对另一个预测器进行训练。当达到所需数量的预测器,或得到完美的预测器时,算法停止。
sklearn使用的时AdaBoost的一个多分类版本,叫做SAMME(基于多类指数损失函数的逐步添加模型)。当只有两个类时,SAMME即等同于AdaBoost。此外,如果预测器可以估算类别概率(即具有predict_proba()方法),sklearn会使用一种SAMME的变体,成为SAMME.R(R代表“Real”),它依赖的是类别概率而不是类别预测,通常表现得更好。
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
ada_clf = AdaBoostClassifier(DecisionTreeClassifier(max_depth=1), n_estimators=200, algorithm="SAMME.R", learning_rate=0.5)
ada_clf.fit(X_train, y_train)
如果你的AdaBoost集成过度拟合训练集,你可以试试减少estimator数量,或提高基础estimator的正则化程度。
梯度提升(Gradient Boosting)
跟AdaBoost一样,梯度提升也是逐渐在集成中添加预测器,每一个都对其前序做出改正。不同之处在于,它不是像AdaBoost那样在每个迭代中调整实例权重,而是让新的预测器针对前一个预测器的残差进行拟合。
使用决策树作为基础预测器,叫做梯度提升树(GBDT)或梯度提升回归树(GBRT)。
GBDT中的树是回归树(不是分类树),GBDT用来做回归预测,调整后也可以用于分类。
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
import matplotlib.pyplot as plt
# 建立数据
boston = load_boston()
X = boston["data"]
y = boston["target"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 首先,在训练集上拟合一个DecisionTreeRegressor
tree_reg1 = DecisionTreeRegressor(max_depth=2)
tree_reg1.fit(X_train, y_train)
# 现在,针对第一个预测器的残差,训练第二个DecisionTreeRegressor
y2 = y_train - tree_reg1.predict(X_train)
tree_reg2 = DecisionTreeRegressor(max_depth=2)
tree_reg2.fit(X_train, y2)
# 然后针对第二个预测器的残差,训练第三个回归器
y3 = y2 - tree_reg2.predict(X_train)
tree_reg3 = DecisionTreeRegressor(max_depth=2)
tree_reg3.fit(X_train, y3)
# 现在,我们有了一个包含3棵树的集成。它将所有树的预测相加,从而对新实例进行预测。
y_pred = sum(tree.predict(X_test) for tree in (tree_reg1, tree_reg2, tree_reg3))
plt.plot(list(range(len(y_pred))), y_pred, 'r', label="pred", alpha=0.5)
plt.plot(list(range(len(y_pred))), y_test, 'b', label="test", alpha=0.5)
plt.legend()
训练GBDT集成有个简单的方法
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0)
gbrt.fit(X_train, y_train)
learning_rate:对每棵树的贡献进行缩放。如果你将其设置为低值,则需要更多的树来拟合训练集,但是预测的泛化效果通常更好。这是一种被称为收缩的正则化技术。如果learning_rate较低,那么estimator太少会导致欠拟合,过多会导致过拟合。
要找到树的最佳数量,可以使用早期停止法。
使用staged_predict()方法:它在训练的每个阶段(一棵树,两棵树…)都对集成的预测返回一个迭代器。
# 以下代码训练了一个拥有120棵树的GBRT集成,然后测量每个训练阶段的验证误差,从而找到树的最优数量,最后使用最优树数重新训练了一个GBRT集成。
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
X_train, X_val, y_train, y_val = train_test_split(X, y)
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=120)
gbrt.fit(X_train, y_train)
errors = [mean_squared_error(y_val, y_pred) for y_pred in gbrt.staged_predict(X_val)]
bst_n_estimators = np.argmin(errors)
gbrt_best = GradientBoostingRegressor(max_depth=2, n_estimators=bst_n_estimators)
gbrt_best.fit(X_train, y_train)
print(bst_n_estimators)
实际上,要实现早期停止法,不一定需要先训练大量的树,然后再回头找最优的数字,还可以真的提前停止训练。设置warm_state=True,当fit()方法被调用时,sklearn会保留现有的树,从而允许增量训练。比如以下代码会在验证误差连续5次迭代未改善时,直接停止训练。
gbrt = GradientBoostingRegressor(max_depth=2, warm_start=True)
min_val_error = float("inf")
error_going_up = 0
for n_estimators in range(1, 120):
gbrt.n_estimators = n_estimators
gbrt.fit(X_train, y_train)
y_pred = gbrt.predict(X_val)
val_error = mean_squared_error(y_val, y_pred)
if val_error < min_val_error:
min_val_error = val_error
error_going_up = 0
else:
error_going_up += 1
if error_going_up == 5:
# 早期停止
break
print(val_error)
print(n_estimators)
subsample:指定用于训练每棵树的实例的比例。如果subsample=0.25,则每棵树用25%的随机选择的实例进行训练。这也是用更高的偏差换取了更低的方差,同时在相当大的程度上加速了训练。这种技术称为随机梯度提升。
loss:梯度提升也可以使用其他成本函数。
堆叠法(stacking,层叠泛化法)
总结
集成学习主要包括(Bagging,Boosting,Stacking) 众所周知,计算方法分为并行,串行,树行,他们分别对应以上三个集成方法 并行的目的在于学习模型的稳定性 串行的目的在于解决并行中出现的泛化能力差之类问题 而树形即(stacking)的思想是什么呢? 个人给出两点: 1.人解决问题的思维是树形的,将模型树行化符合问题本身的逻辑,精确率和召回率呈稳态正相关 2.stacking使模型的融合更科学化,分层预测的计算结果远优于向量均值化和投票机制。
练习摘抄
硬投票分类器和软投票分类器有什么区别?
硬投票分类器只是统计每个分类器的投票,然后挑选出得票最多的类别。软投票分类器计算出每个类别的平均估算概率,然后选出概率最高的类别。它比硬投票法的表现更优,因为它给予那些高度自信的投票更高的权重。但是它要求每个分类器都能够估算出类别概率才可以正常工作。(例如,sklearn中的SVM分类器必须要设置probability=True)
是否可以通过在多个服务器上并行来加速bagging集成的训练?pasting集成呢?boosting集成呢?随机森林或stacking集成呢?
对于bagging集成来说,将其分布在多个服务器上能够有效加速训练过程,因为集成中的每个预测器都是独立工作的。
同理,对于pasting集成和随机森林来说也是如此。
但是,boosting集成的每个预测器都是基于前序的结果,因此训练过程必须是有序的,将其分布在多个服务器上毫无意义。
对于stacking集成来说,某个指定层的预测器之间彼此独立,因而可以在多台服务器上并行训练,但是某一层的预测器只能在其前一层的预测器全部训练完成之后,才能开始训练。
包外评估的好处是什么?
包外评估可以对bagging集成中的每个预测器使用其未经训练的实例进行评估。不需要额外的验证集,就可以对集成实施相当公正的评估。所以,如果训练使用的实例越多,集成的性能可以略有提升。
是什么让极端随机树比一般随机森林更加随机?这部分增加的随机性有什么用?极端随机树比一般随机森林快还是慢?
随机森林在生长过程中,每个节点的分裂仅考虑了特征的一个随机子集。极限随机树也是如此,它甚至走得更远:常规随机树会搜索出特征的最佳阈值,极限随机树直接对每个特征使用随机阈值。这种极限随机性就像是一种正则化的形式:如果随机森林对训练数据出现过拟合,那么极限随机树可能执行效果更好。更甚的是,极限随机树不需要计算最佳阈值,因此它训练起来比随机森林快得多。但是,在做预测的时候,相比随机森林它不快也不慢。
如果你的AdaBoost集成队训练数据拟合不足,你应该调整哪些超参数?怎么调整?
如果你的梯度提升集成对训练集过度拟合,你应该提升还是降低学习率?
降低学习率,也可以通过早停法来寻找合适的预测器数量(可能是因为预测器太多)。
数据降维的两种主要方法
PCA
主成分分析(PCA)是迄今为止最流行的降维算法。它先是识别出最接近数据的超平面,然后将数据投影其上。
比较原始数据集与其轴上的投影之间的均方距离,使这个均方距离最小的轴使最合理的选择。这也正是PCA背后的简单思想。
奇异值分解(SVD)
import numpy as np
X = np.random.random((50, 2))
X_centered = X - X.mean(axis=0)
U, s, V = np.linalg.svd(X_centered)
c1 = V.T[:, 0]
c2 = V.T[:, 1]
W2 = V.T[:, :1]
W2D = X_centered.dot(W2)
PCA实战
from sklearn.decomposition import PCA
pca = PCA(n_components=1)
X2D = pca.fit_transform(X)
components_:访问主成分(它包含的主成分使水平向量)。举例来说,第一个主成分即等于
pca.components_.T[:, 0]
explained_variance_ratio_:方差解释率。它表示每个主成分轴对整个数据集方差的贡献度。
inverse_transform():将压缩后的数据解压缩回到原来的shape。原始数据和重建数据(压缩之后解压缩)之间的均方距离成为重建误差。
选择正确数量的维度:
增量PCA(IPCA)
增量主成分分析
可以将训练集分成一个个小批量,一次给IPCA
import numpy as np
from sklearn.decomposition import IncrementalPCA
from sklearn.datasets import fetch_mldata
n_batches = 100
mnist = fetch_mldata('MNIST original')
X_mnist = mnist["data"]
inc_pca = IncrementalPCA(n_components=154)
for X_batch in np.array_split(X_mnist, n_batches):
inc_pca.partial_fit(X_batch)
X_mnist_reduced = inc_pca.transform(X_mnist)
还可以使用Numpy的memmap类,它允许你巧妙地操控一个存储在磁盘二进制文件的大型数组,就好似它也完全在内存里一样,而这个类(memmap)仅在需要时加载内存中需要的数据。
内存映射,当需要存取一个很大的文件里面的小部分的数据的时候,读入整个文件显然是非常的浪费资源的。于是要使用到内存映射的方法。
numpy中的numpy.memmap函数的用法
X_mm = np.memmap(filename, dtype="float32", mode="readonly", shape=(m, n))
batch_size = m // n_batches
inc_pca = IncrementalPCA(n_components=154, batch_size=batch_size)
inc_pca.fit(X_mm)
随机PCA
核主成分分析(kPCA)
将核技巧应用于PCA,使复杂的非线性投影降维成为可能。
它擅长在投影后保留实例的集群,有时甚至也能展开近似于一个扭曲流形的数据集。
from sklearn.decomposition import KernelPCA
rbf_pca = KernelPCA(n_components=2, kernel='rbf', gamma=0.04)
X_reduced = rbf_pca.fit_transform(X)
选择核函数核调整超参数
使用网格搜索,kPCA搭配分类器(逻辑回归等),来找到使任务性能最佳的核和超参数。
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.decomposition import KernelPCA
from sklearn.pipeline import Pipeline
import numpy as np
clf = Pipeline([
("kpca", KernelPCA(n_components=2)),
("log_reg", LogisticRegression())
])
param_grid = [{
"kpca__gamma": np.linspace(0.03, 0.05, 10),
"kpca__kernel": ["rbf", "sigmoid"]
}]
grid_search = GridSearchCV(clf, param_grid, cv=3)
grid_search.fit(X, y)
重建原像
还有一种完全不受监督的方法,就是选择重建误差最低的核和超参数。
如果我们对一个已经降维的实例进行线性PCA逆转换,重建的点将存在于特征空间,而不是原始空间中。而这里特征空间是无限维度的,所以我们无法计算出重建点,因此也无法计算出真实的重建误差。
我们可以在原始空间中找到一个点,使其映射接近于重建点。这被称为重建原像。
一旦有了这个原像,你就可以测量它到原始实例的平方距离。最后,便可以选择使这个重建原像误差最小化的核和超参数。
**执行重建的方法:**训练一个监督式回归模型,以投影后的实例作为训练集,并以原始实例作为目标。(设置KernelPCA中的fit_inverse_transform=True)
rbf_pca = KernelPCA(n_components=2, kernel="rbf", gamma=0.0433, fit_inverse_transform=True)
X_reduced = rbf_pca.fit_transform(X)
X_preimage = rbf_pca.inverse_transform(X_reduced)
计算重建原像误差:
from sklearn.metrics import mean_squared_error
mean_squared_error(X, X_preimage)
现在,你可以使用交叉验证的网格搜索,来寻找使这个原像重建误差最小的核和超参数。
局部线性嵌入(LLE,Locally Linear Embedding)
局部线性嵌入使另一种非常强大的非线性降维(NLDR)技术,不像之前的算法依赖于投影,它是一种流形学习技术。
LLE首先测量每个算法如何与其最近的邻居线性相关,然后为训练集寻找一个能最大程度保留这些局部关系的低维表示。
这使得它特别擅长展开弯曲的流形,特别使没有太多噪声时。
from sklearn.manifold import LocallyLinearEmbedding
lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10)
X_reduced = lle.fit_transform(X)
计算复杂度:
其他降维技巧
练习摘抄
降低数据集维度的主要动机是什么?有什么主要弊端?
降维的主要动机是:
主要的弊端使:
什么是维度的诅咒?
维度的诅咒是指寻多在低维空间中不存在的问题,在高维空间中发生。在机器学习领域,一个常见的现象是随机抽样的高维向量通常非常稀疏,提升了过度拟合的风险,同时也使得在没有充足训练数据的情况下,要识别数据中的模式非常困难。
一旦数据集被降维,是否还有可能逆转?如果有,怎么做?如果没有,为什么?
一旦使用我们讨论的任意算法减少了数据集的维度,几乎不可能再将操作完美的逆转,因为在降维过程中必然丢失了一部分信息。虽然有一些算法(例如PCA)拥有简单的逆转换过程,可以重建出与原始数据集相似的数据集,但是也有一些算法不能实现逆转(例如t-SNE)。
PCA可以用来给高度非线性数据集降维么?
对大多数数据集来说,PCA可以用来进行显著降维,即便是高度非线性的数据集,因为它至少可以消除无用的维度。但是如果不存在无用的维度(例如瑞士卷),那么使用PCA降维将会损失太多信息。你希望的是将瑞士卷展开,而不是将其压扁。
常规PCA,增量PCA,随机PCA及核PCA各适用于何种情况?
如何在你的数据集上评估降维算法的性能?
链接两个不同的降维算法有意义吗?
链接两个不同的降维算法绝对是有意义的。常见的例子是使用PCA快速去除大量无用的维度,然后应用另一种更慢的降维算法,如LLE。这样两步走的策略产生的结果可能与仅使用LLE相同,但是时间要短得多。
创建一个计算图并在会话中执行
x = tf.Variable(3, name="x")
y = tf.Variable(4, name="y")
f = x*x*y + y + 2
sess = tf.Session()
sess.run(x.initializer)
sess.run(y.initializer)
result = sess.run(f)
print(result)
sess.close()
# 每次重复sess.run()看起来有些笨拙,好在有更好的方式
with tf.Session() as sess:
x.initializer.run()
y.initializer.run()
result = f.eval()
print(result)
其中x.initializer.run()等价于tf.get_default_session().run(x.initializer)
同样,f.eval()等价于tf.get_default_session().run(f)
除了手工为每个变量调用初始化器之外,还可以使用global_variables_initializer()
函数来完成同样的动作。注意:这个操作并不会立刻做初始化,它只是在图中创建了一个节点,这个节点会在会话执行时初始化所有变量。
init = tf.global_variables_initializer()
with tf.Session() as sess:
init.run()
result = f.eval()
print(result)
在Jupyter或者Python shell中,可以创建一个InteractiveSession。它和常规会话的不同之处在于InteractiveSession在创建时会将自己设置为默认会话,因此你无须使用with块(不过需要在结束之后手动关闭会话)。
sess = tf.InteractiveSession()
init.run()
result = f.eval()
print(result)
sess.close()
管理图
你创建的所有节点都会自动添加到默认图上
x1 = tf.Variable(1)
x1.graph is tf.get_default_graph()
有时候你可能想要管理多个互不依赖的图。可以创建一个新的图,然后用with块临时将它设置为默认图
graph = tf.Graph()
with graph.as_default():
x2 = tf.Variable(2)
x2.graph is graph
x2.graph is tf.get_default_graph()
在Jupyter中,做实验时你经常会多次执行同一条命令。这样可能会在同一个图上添加了很多重复的节点。
节点值的生命周期
w = tf.constant(3)
x = w + 2
y = x + 5
z = x * 3
with tf.Session() as sess:
print(y.eval())
print(z.eval())
TensorFlow不会重复用上一步求值的w和x的结果。简而言之,w和x的值会被计算两次。
在图的每次执行间,所有节点值都会被丢弃,但是变量的值不会,因为变量的值是由会话维护的(队列和阅读器也会维护一些状态)。变量的生命周期从初始化器的执行开始,到关闭会话才结束。
对于上述代码,如果你不希望对y和z重复求值,那么必须告诉TensorFlow在一次图执行中就完成y和z的求值
with tf.Session() as sess:
y_val, z_val = sess.run([y, z])
print(y_val)
print(z_val)
在单进程的TensorFlow中,即使它们共享同一个计算图,多个会话之间仍然互相隔离,不共享任何状态(每个会话对每个变量都有自己的拷贝)。
对于分布式TensorFlow,变量值保存在每个服务器上,而不是会话中,所以多个会话可以共享同一变量。
TensorFlow中的线性回归
import numpy as np
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()
m, n = housing.data.shape
housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
X = tf.constant(housing_data_plus_bias, dtype=tf.float32, name="X")
y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name="y")
XT = tf.transpose(X)
theta = tf.matmul(tf.matmul(tf.matrix_inverse(tf.matmul(XT, X)), XT), y)
with tf.Session() as sess:
theta_value = theta.eval()
print(theta_value)
实现梯度下降
手动计算梯度
n_epochs = 1000
learning_rate = 0.01
X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name="X")
y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name="y")
theta = tf.Variable(tf.random_uniform([n+1, 1], -1.0, 1.0), name="theta")
y_pred = tf.matmul(X, theta, name="perdictions")
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
gradients = 2/m * tf.matmul(tf.transpose(X), error)
training_op = tf.assign(theta, theta - learning_rate * gradients)
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for epoch in range(n_epochs):
if epoch % 100 == 0:
print("Epoch", epoch, "MSE = ", mse.eval())
sess.run(training_op)
best_theta = theta.eval()
使用自动微分
TensorFlow的autodiff功能可以自动而且高效的算出梯度。只需要把上述例子中的对gradients的赋值的语句换成下面的代码即可。
gradients = tf.gradients(mse, [theta])[0]
gradients()函数接受一个操作符(这里是mse)和一个参数列表(这里是theta)作为参数,然后它会创建一个操作符的列表来计算每个变量的梯度。所以梯度节点将计算MSE相对于theta的梯度向量。
TensorFlow使用了反向的autodiff算法,它非常适用于有多个输入和少量输出的场景,在神经网络中这种场景非常常见。它只需要 n o u t p u t s + 1 n_{outputs}+1 noutputs+1次遍历,就可以求出所有输出相对于输入的偏导。
四种自动计算梯度的主要方法
方法 | 精确度 | 是否支持任意代码 | 备注 |
---|---|---|---|
数值微分 | 低 | 是 | 实现琐碎 |
符号微分 | 高 | 否 | 会构建一个完全不同的图 |
前向自动微分 | 高 | 是 | 基于二元树 |
反向自动微分 | 高 | 是 | 由TensorFlow实现 |
实现优化器
TensorFlow会帮你计算梯度,不过它还提供更容易的方法:它内置了很多的优化器,其中就包含梯度下降优化器。你只需要把上面对gradients=…和training_op=…赋值的语句修改成下面的代码即可:
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)
动量优化器(比梯度下降优化器的收敛速度快很多)
optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate, momentum=0.9)
给训练算法提供数据
占位符节点
A = tf.placeholder(tf.float32, shape=(None, 3))
B = A + 5
with tf.Session() as sess:
B_val_1 = B.eval(feed_dict={
A: [[1, 2, 3]]})
B_val_2 = B.eval(feed_dict={
A: [[4, 5, 6], [7, 8, 9]]})
print(B_val_1)
print(B_val_2)
Mini-batch Gradient Descent
learning_rate = 0.01
X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")
y_pred = tf.matmul(X, theta, name="predictions")
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)
init = tf.global_variables_initializer()
n_epochs = 10
batch_size = 100
n_batches = int(np.ceil(m / batch_size))
def fetch_batch(epoch, batch_index, batch_size):
np.random.seed(epoch * n_batches + batch_index) # not shown in the book
indices = np.random.randint(m, size=batch_size) # not shown
X_batch = scaled_housing_data_plus_bias[indices] # not shown
y_batch = housing.target.reshape(-1, 1)[indices] # not shown
return X_batch, y_batch
with tf.Session() as sess:
sess.run(init)
for epoch in range(n_epochs):
for batch_index in range(n_batches):
X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
best_theta = theta.eval()
保存和恢复模型
保存模型:saver = tf.train.Saver()
n_epochs = 1000 # not shown in the book
learning_rate = 0.01 # not shown
X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name="X") # not shown
y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name="y") # not shown
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")
y_pred = tf.matmul(X, theta, name="predictions") # not shown
error = y_pred - y # not shown
mse = tf.reduce_mean(tf.square(error), name="mse") # not shown
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate) # not shown
training_op = optimizer.minimize(mse) # not shown
init = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
sess.run(init)
for epoch in range(n_epochs):
if epoch % 100 == 0:
print("Epoch", epoch, "MSE =", mse.eval()) # not shown
save_path = saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model.ckpt")
sess.run(training_op)
best_theta = theta.eval()
save_path = saver.save(sess, "D:/Python3Space/BookStudy/book4/model/tmp/my_model_final.ckpt")
恢复模型:恢复模型同样简单,与之前一样,在构造期末尾创建一个Saver节点,不过在执行期开始的时候,不是用init节点来初始化变量,而是调用Saver对象上的restore()方法。
with tf.Session() as sess:
saver.restore(sess, "D:/Python3Space/BookStudy/book4/model/tmp/my_model_final.ckpt")
best_theta_restored = theta.eval() # not shown in the book
print(best_theta_restored)
默认的,Saver会按照变量名来保留和恢复变量,不过如果你想做更多的控制,也可以在保存和恢复时自己指定名称。比如,在下面的代码中,Saver只会保存theta,并将其命名为weights:
saver = tf.train.Saver({"weights": theta})
用TensorBoard来可视化图和训练曲线
开启TensorBoard:tensorboard --logdir tf_logs/
from datetime import datetime
now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
root_logdir = "D:/Python3Space/BookStudy/book4/model/tf_logs"
logdir = "{}/run-{}/".format(root_logdir, now)
n_epochs = 1000
learning_rate = 0.01
X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")
y_pred = tf.matmul(X, theta, name="predictions")
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)
init = tf.global_variables_initializer()
mse_summary = tf.summary.scalar('MSE', mse)
file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())
n_epochs = 10
batch_size = 100
n_batches = int(np.ceil(m / batch_size))
with tf.Session() as sess: # not shown in the book
sess.run(init) # not shown
for epoch in range(n_epochs): # not shown
for batch_index in range(n_batches):
X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
if batch_index % 10 == 0:
summary_str = mse_summary.eval(feed_dict={
X: X_batch, y: y_batch})
step = epoch * n_batches + batch_index
file_writer.add_summary(summary_str, step)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
best_theta = theta.eval() # not shown
file_writer.close()
命名作用域
reset_graph()
now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
root_logdir = "tf_logs"
logdir = "{}/run-{}/".format(root_logdir, now)
n_epochs = 1000
learning_rate = 0.01
X = tf.placeholder(tf.float32, shape=(None, n + 1), name="X")
y = tf.placeholder(tf.float32, shape=(None, 1), name="y")
theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0, seed=42), name="theta")
y_pred = tf.matmul(X, theta, name="predictions")
with tf.name_scope("loss") as scope:
error = y_pred - y
mse = tf.reduce_mean(tf.square(error), name="mse")
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(mse)
init = tf.global_variables_initializer()
mse_summary = tf.summary.scalar('MSE', mse)
file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())
n_epochs = 10
batch_size = 100
n_batches = int(np.ceil(m / batch_size))
with tf.Session() as sess:
sess.run(init)
for epoch in range(n_epochs):
for batch_index in range(n_batches):
X_batch, y_batch = fetch_batch(epoch, batch_index, batch_size)
if batch_index % 10 == 0:
summary_str = mse_summary.eval(feed_dict={
X: X_batch, y: y_batch})
step = epoch * n_batches + batch_index
file_writer.add_summary(summary_str, step)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
best_theta = theta.eval()
file_writer.flush()
file_writer.close()
print("Best theta:")
print(best_theta)
print(error.op.name)
print(mse.op.name)
模块化
一段错误的代码,这段代码中包含了一个cut-and-paste的错误
import tensorflow as tf
import numpy as np
n_features = 3
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
w1 = tf.Variable(tf.random_normal((n_features, 1)), name="weights1")
w2 = tf.Variable(tf.random_normal((n_features, 1)), name="weights2")
b1 = tf.Variable(0.0, name="bias1")
b2 = tf.Variable(0.0, name="bias2")
z1 = tf.add(tf.matmul(X, w1), b1, name="z1")
z2 = tf.add(tf.matmul(X, w2), b2, name="z2")
relu1 = tf.maximum(z1, 0., name="relu1")
relu2 = tf.maximum(z1, 0., name="relu2")
output = tf.add(relu1, relu2, name="output")
TensorFlow会让你保持DRY(Don’t Repeat Yourself,不要重复自己)原则:用一个函数来构建ReLU。
def reset_graph(seed=42):
tf.reset_default_graph()
tf.set_random_seed(seed)
np.random.seed(seed)
def relu(X):
with tf.name_scope("relu"):
w_shape = (int(X.get_shape()[1]), 1)
w = tf.Variable(tf.random_normal(w_shape), name="weights")
b = tf.Variable(0.0, name="bias")
z = tf.add(tf.matmul(X, w), b, name="z")
return tf.maximum(z, 0., name="relu")
reset_graph()
n_features = 3
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
relus = [relu(X) for i in range(5)]
output = tf.add_n(relus, name="output")
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
result = output.eval(feed_dict={
X: [[1, 2, 3], [4, 5, 6]]})
print(result)
file_writer.close()
共享变量
如果你想在图的不同组建中共享变量:最简单的做法是先创建,然后将其作为参数传递给需要它的函数。
TensorFlow提供另外一个选择,可以让代码更加清晰,也更加模块化。
如果共享变量不存在,该方法先通过get_variable()函数创建共享变量;如果已经存在了,就复用该共享变量。期望的行为通过当前variable_scope()的一个属性来控制(创建或者复用)。
with tf.variable_scope("relu"):
threshold = tf.get_variable("threshold", shape=(), initializer=tf.constant_initializer(0.0))
注意,如果这个变量之前已经被get_variable()调用创建过,这里会抛出一个一场。这种机制避免由于误操作而复用变量。
如果要复用一个变量,需要通过设置变量作用域reuse属性为True来显示地实现(在这里,不必指定形状或初始化器)。
with tf.variable_scope("relu", reuse=True):
threshold = tf.get_variable("threshold")
这段代码会获取既有的“relu/threshold”变量,如果该变量不存在,或者在调用get_variable()时没有创建成功,那么会抛出一个异常。
另一种方式是,在调用作用域的reuse_variables()方法块中设置reuse属性为True。
with tf.variable_scope("relu") as scope:
scope.reuse_variables()
threshold = tf.get_variable("threshold")
一旦reuse属性设置为True之后,在该块中就不能再设置为False了。另外,如果在块中定义了另外的变量作用域,它们会自动继承resue=True。最后,只有通过get_variable()创建的变量才可以用这种方式进行复用。
现在你已经看到了所有能让relu()函数无须传入参数就访问threshold变量的方法了:
reset_graph()
def relu(X):
with tf.variable_scope("relu", reuse=True):
threshold = tf.get_variable("threshold")
w_shape = int(X.get_shape()[1]), 1 # not shown
w = tf.Variable(tf.random_normal(w_shape), name="weights") # not shown
b = tf.Variable(0.0, name="bias") # not shown
z = tf.add(tf.matmul(X, w), b, name="z") # not shown
return tf.maximum(z, threshold, name="max")
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
with tf.variable_scope("relu"):
threshold = tf.get_variable("threshold", shape=(),
initializer=tf.constant_initializer(0.0))
relus = [relu(X) for relu_index in range(5)]
output = tf.add_n(relus, name="output")
file_writer = tf.summary.FileWriter("D:/李添的数据哦!!!/BookStudy/book4/model/logs/relu6", tf.get_default_graph())
file_writer.close()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
result = output.eval(feed_dict={
X: [[1, 2, 3], [4, 5, 6]]})
print(result)
通过get_variable()创建的变量总是以它们的variable_scope作为前缀来命名的(比如“relu/threshold”),对于其它节点(包括通过tf.Variable()创建的变量)变量作用域的行为就好像是一个新的作用域。具体来说,如果一个命名作用域有一个已经创建了的变量名,那么就会加上一个后缀以保证其唯一性。比如,上面的例子中的所有变量(除了threshold变量)都有一个“relu_1”到“relu_5”的前缀。
遗憾的是,threshold变量必须定义在relu()函数之外,其他所有的ReLU代码都在内部。要解决这个问题,下面的代码在relu()函数第一次调用时创建了threshold变量,并在后续的调用中复用。现在relu()函数无须关注命名作用域或者变量共享问题,它只需要调用get_variable(),来创建或者复用threshold变量(无需关心到底是创建还是复用)。剩下的代码调用了relu()5次,确保第一次调用时将reuse设置为False,后续的调用将reuse设置为True。
结果跟之前的略有不同,因为共享变量在第一个ReLU中。
方法1:
reset_graph()
def relu(X):
with tf.variable_scope("relu"):
threshold = tf.get_variable("threshold", shape=(), initializer=tf.constant_initializer(0.0))
w_shape = (int(X.get_shape()[1]), 1)
w = tf.Variable(tf.random_normal(w_shape), name="weights")
b = tf.Variable(0.0, name="bias")
z = tf.add(tf.matmul(X, w), b, name="z")
return tf.maximum(z, threshold, name="max")
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
with tf.variable_scope("", default_name="") as scope:
first_relu = relu(X) # create the shared variable
scope.reuse_variables() # then reuse it
relus = [first_relu] + [relu(X) for i in range(4)]
output = tf.add_n(relus, name="output")
file_writer = tf.summary.FileWriter("D:/李添的数据哦!!!/BookStudy/book4/model/logs/relu8", tf.get_default_graph())
file_writer.close()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
result = output.eval(feed_dict={
X: [[1, 2, 3], [4, 5, 6]]})
print(result)
方法2:
```python
reset_graph()
def relu(X):
threshold = tf.get_variable("threshold", shape=(),
initializer=tf.constant_initializer(0.0))
w_shape = (int(X.get_shape()[1]), 1) # not shown in the book
w = tf.Variable(tf.random_normal(w_shape), name="weights") # not shown
b = tf.Variable(0.0, name="bias") # not shown
z = tf.add(tf.matmul(X, w), b, name="z") # not shown
return tf.maximum(z, threshold, name="max")
X = tf.placeholder(tf.float32, shape=(None, n_features), name="X")
relus = []
for relu_index in range(5):
with tf.variable_scope(“relu”, reuse=(relu_index >= 1)) as scope:
relus.append(relu(X))
output = tf.add_n(relus, name=“output”)
file_writer = tf.summary.FileWriter("D:/李添的数据哦!!!/BookStudy/book4/model/logs/relu9", tf.get_default_graph())
file_writer.close()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
result = output.eval(feed_dict={X: [[1, 2, 3], [4, 5, 6]]})
print(result)
```
练习摘抄
语句a_val, b_val = a.eval(session=sess), b.eval(session=sess)和a_val, b_val = sess.run([a, b])等价吗?
不等价。第一条语句会运行两次(第一次计算a,第二次计算b),而第二条语句只运行一次。如果这些操作(或者它们依赖的操作)中的任意一个具有副作用(比如,修改一个变量,向队列中插入一条记录,或者读取一个文件等),那么效果就会不同。如果操作没有副作用,那么语句会返回同样的结果,不过第二条语句会比第一条快。
假设你创建了一个包含变量w的图,然后在两个线程中分别启动一个会话,两个线程都使用了图g,每个会话会有自己对w变量的拷贝,还是会共享变量?
在本地TensorFlow中,会话用来管理变量的值,如果你创建了一个包含变量w的图g,然后启动两个线程,并在每个线程中打开一个本地的会话,这两个线程使用同一个图g,那么每个会话会拥有自己的w的拷贝。如果在分布式的TensorFlow中,变量的值则存储在由集群管理的容器中,如果两个会话连接了同一个集群,并使用同一个容器,那么它们会共享变量w。
变量何时被初始化,又在何时被销毁?
变量在调用其初始化器的时候被初始化,在会话结束的时候被销毁。在分布式TensorFlow中,变量存活于集群上的容器中,所以关闭一个会话不会销毁变量。要销毁一个变量,你需要清空它所在的容器。
占位符和变量的区别是什么?
在执行期,你如何为一个变量设置任意的值?
你可以在构造一个图时指定变量的初始值,它会在后边的执行期运行变量的初始化器的时候被初始化。如果你想在执行期修改变量的值,那么最简单的方法是使用tf.assign函数创建一个赋值节点(在图的构造器),将变量和一个占位符传入作为参数。这样,你可以在执行期运行赋值操作来为变量传入新值。
反向模式autodiff需要多少次遍历图形才能计算10个变量的成本函数的梯度?正向模式autodiff怎么样?符号微分呢?
要计算任意数量变量的成本函数的梯度,反向模式的autodiff算法(由TensorFlow实现)只需要遍历两次图。作为对比,正向模式的autodiff算法需要为每个变量运行一次(如果我们需要10个不同变量的梯度,那么就需要执行10次)。对于符号微分,它会建立一个不同的图来计算梯度,所以根本不会遍历原来的图(除了在建立新的梯度图时)。一个高度优化的符号微分系统可能只需要运行一次新的梯度图来计算和所有变量相关的梯度,但与原始图相比,新的图可能时非常复杂和低效的。
感知器
线性阈值单元(LTU)
import numpy as np
from sklearn.datasets import load_iris
from sklearn.linear_model import Perceptron
iris = load_iris()
X = iris.data[:, (2, 3)]
y = (iris.target == 0).astype(np.int)
per_clf = Perceptron(random_state=42)
per_clf.fit(X, y)
y_pred = per_clf.predict([[2, 0.5]])
print(y_pred)
事实上,在sklearn中,Perceptron类的行为等同于使用以下超参数的SGDClassifier:
loss="perceptron", learning_rate="constant", eta0=1(学习速率), penalty=None(不做正则化)
注意和逻辑回归分类器相反,感知器不输出某个类的概率,它只能根据一个固定的阈值来做预测。这也是更应该使用逻辑回归而不是感知器的一个原因。
感知器无法处理一些很微小的问题,比如异或分类问题(XOR),不过事实证明感知器的一些限制可以通过多层感知器来解决(Multi-Layer Perceptron,MLP)
多层感知器和反向传播
使用TensorFlow的高级API来训练MLP
用DNNClassifier类来训练一个有着任意数量隐藏层,并包含一个用来计算类别概率的sofmax输出层的神经网络。
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist = input_data.read_data_sets("D:/李添的数据哦!!!/BookStudy/book4/MNIST_data/MNIST_data/")
X_train = mnist.train.images
X_test = mnist.test.images
y_train = mnist.train.labels.astype("int")
y_test = mnist.test.labels.astype("int")
config = tf.contrib.learn.RunConfig(tf_random_seed=42) # not shown in the config
feature_cols = tf.contrib.learn.infer_real_valued_columns_from_input(X_train)
dnn_clf = tf.contrib.learn.DNNClassifier(hidden_units=[300,100], n_classes=10,
feature_columns=feature_cols, config=config)
dnn_clf = tf.contrib.learn.SKCompat(dnn_clf) # if TensorFlow >= 1.1
dnn_clf.fit(X_train, y_train, batch_size=50, steps=40000)
模型评估
from sklearn.metrics import accuracy_score
y_pred = dnn_clf.predict(X_test)
accuracy_score(y_test, y_pred['classes'])
from sklearn.metrics import log_loss
y_pred_proba = y_pred['probabilities']
log_loss(y_test, y_pred_proba)
库还包含了一些方便的函数来评估模型:
dnn_clf.score(X_test, y_test)
使用纯TensorFlow训练DNN
构建阶段
创建用于输入和目标值占位符节点
import tensorflow as tf
import numpy as np
n_inputs = 28*28
n_hidden1 = 300
n_hidden2 = 100
n_outputs = 10
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")
创建用以创建神经网络的函数,使用它创建DNN
手动
def neuron_layer(X, n_neurons, name, activation=None):
with tf.name_scope(name):
n_inputs = int(X.get_shape()[1])
stddev = 2 / np.sqrt(n_inputs)
init = tf.truncated_normal((n_inputs, n_neurons), stddev=stddev)
W = tf.Variable(init, name="weights")
b = tf.Variable(tf.zeros([n_neurons]), name="biases")
z = tf.matmul(X, W) + b
if activation == "relu":
return tf.nn.relu(z)
else:
return z
with tf.name_scope("dnn"):
hidden1 = neuron_layer(X, n_hidden1, "hidden1", activation="relu")
hidden2 = neuron_layer(hidden1, n_hidden2, "hidden2", activation="relu")
logits = neuron_layer(hidden2, n_outputs, "outputs")
利用fully_connected()函数来替换自己写的neuron_layer()函数
from tensorflow.contrib.layers import fully_connected
with tf.name_scope("dnn"):
hidden1 = fully_connected(X, n_hidden1, scope="hidden_1")
hidden2 = fully_connected(hidden1, n_hidden2, scope="hidden_2")
logits2 = fully_connected(hidden2, n_outputs, scope="outputs_", activation_fn=None)
定义成本函数
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits2)
loss = tf.reduce_mean(xentropy, name="loss")
创建优化器
learning_rate = 0.01
with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
定义性能度量
with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits2, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))
最后初始化和保存模型
init = tf.global_variables_initializer()
saver = tf.train.Saver()
tf.truncated_normal((n_inputs, n_neurons), stddev=np.sqrt(n_inputs))
:使用标准偏差为2/sqrt(n_inputs)的阶段正态(高斯)分布进行随机初始化。使用截断的正态分布而不是常规的正态分布,保证这里不存在任何减慢训练的大权重。使用一个指定的标准偏差会让算法收敛得更快。
为所有隐藏层随机地初始化连接权重值是非常重要的,这可以避免任何可能导致梯度下降出现无法终止的对称性。
tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
:它会根据“logits”来计算交叉熵(比如,在通过softmax激活函数之前网络的输出),并且期望以0到分类个数减1的整数形式标记。这会计算出一个包含每个实例的交叉熵的一维张量。可以用TensorFlow的reduce_mean()函数来计算所有实例的平均交叉熵。
函数sparse_softmax_cross_entropy_with_logits()
与先应用softmax函数再计算交叉熵的效果是一样的,不过它更高效一些,另外它会处理一些边界值如logits等于0的情况。这也是为什么我们之前没有使用softmax激活函数的原因。此外,还有一个softmax_cross_entropy_with_logits()
函数,它以one-hot的形式获取标签(而不是从0到分类数量-1)
执行阶段
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("D:/李添的数据哦!!!/BookStudy/book4/MNIST_data/MNIST_data/")
n_epochs = 400
batch_size = 50
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
for iteration in range(mnist.train.num_examples // batch_size):
X_batch, y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
acc_train = accuracy.eval(feed_dict={
X: X_batch, y: y_batch})
acc_test = accuracy.eval(feed_dict={
X: mnist.test.images, y: mnist.test.labels})
print(epoch, "Train accuracy:", acc_train, "Test accuracy:", acc_test)
save_path = saver.save(sess, "D:/李添的数据哦!!!/BookStudy/book4/model/my_model_final.ckpt")
使用神经网络
with tf.Session() as sess:
saver.restore(sess, "D:/李添的数据哦!!!/BookStudy/book4/model/my_model_final.ckpt")
X_new_scaled = X_test[0].reshape(1, -1)
Z = logits.eval(feed_dict={
X: X_new_scaled})
y_pred = np.argmax(Z, axis=1)
微调神经网络的超参数
隐藏层的个数
每个隐藏层中的神经元数
激活函数
练习摘抄
为什么通常更倾向用逻辑回归分类器而不是经典的感知器?如何调整一个感知器,让它与逻辑回归分类器等价?
经典的感知器只有在数据集是线性可分的情况下才会收敛,并且不能估计分类的概率。作为对比,逻辑回归分类器即使在数据集不是线性可分的情况下也可以很好的收敛,而且还能输出分类的概率。
如果你将感知器的激活函数修改为逻辑激活函数(或者如果有多个神经元的时候,采用softmax激活函数),然后训练其使用梯度下降(或者使成本函数最小化的一些其他优化算法,通常使交叉熵法),那么它就会变成一个逻辑回归分类器了。
为什么逻辑激活函数是训练第一个MLP的关键因素?
因为它的导数总是非零的,所以梯度下降总是可以持续的。当激活功能是一个阶梯函数时,渐变下降就不能再持续了,因为这时候根本没有斜率。
什么时反向传播,它时如何工作的?反向传播与反式自动微分有何区别?
反向传播是一种用于训练人工神经网络的技术。它首先计算关于每个模型参数(所有的权重和偏差)的成本函数的梯度,然后使用这些梯度执行梯度下降。这种反向传播步骤通常执行数千次或数百万次,并需要多个训练批次,直到模型参数收敛到最小化成本函数的值为止。为了计算梯度,反向传播使用反向模式autodiff(尽管在反向传播被发明的时候还不叫autodiff,事实上autodiff的概念已经被重新发明了多次)。
反向模式的autodiff会先在计算图上正向执行一次,计算按当前训练批次的每个节点的值,然后反向执行一次,一次性计算所有梯度。
那和反向传播有什么区别呢?反向传播是指使用多个反向传播步骤来训练人工神经网络的全部过程,每个步骤计算梯度并使用它们执行梯度下降的过程。相反,反向模式autodiff只是一种简单的计算梯度的技术,只是恰好被反向传播使用了而已。
梯度消失/爆炸问题
Xavier初始化和He初始化
在Glorot和Bengio的论文中提出,当预测的时候要保持正向,在反向传播梯度的时保持反方向。为了让信号正确流动,作者提出需要保持每一层的输入和输出的方差一致,并且需要在反向流动过某一层时,前后的方差也要一致。事实上,这是很难保证的,除非一层有相同数量的输入和输出连接。因此它们提出了一个很好的这种方案:
连接的权重必须按照以下公式进行随机初始化,其中 n i n p u t s n_{inputs} ninputs和 n o u t p u t s n_{outputs} noutputs是权重被初始化层的输入和输出连接数(也称为扇入和扇出)。这种初始化方法称为Xavier初始化(当使用逻辑激活函数时)。同理,ReLU激活函数的初始化方法及其变种称为He初始化。
激活函数 | 均匀分布-r、r | 正态分布 |
---|---|---|
逻辑函数 | r = 6 n i n p u t s + n o u t p u t s r=\sqrt{ \frac{6}{n_{inputs} + n_{outputs}} } r=ninputs+noutputs6 | σ = 2 n i n p u t s + n o u t p u t s \sigma=\sqrt{ \frac{2}{n_{inputs} + n_{outputs}} } σ=ninputs+noutputs2 |
双曲正切函数 | r = 6 n i n p u t s + n o u t p u t s 4 r=\sqrt[4]{ \frac{6}{n_{inputs} + n_{outputs}} } r=4ninputs+noutputs6 | σ = 2 n i n p u t s + n o u t p u t s 4 \sigma=\sqrt[4]{ \frac{2}{n_{inputs} + n_{outputs}} } σ=4ninputs+noutputs2 |
ReLU(及变种) | r = 6 n i n p u t s + n o u t p u t s 2 r=\sqrt[\sqrt2]{ \frac{6}{n_{inputs} + n_{outputs}} } r=2ninputs+noutputs6 | σ = 2 n i n p u t s + n o u t p u t s 2 \sigma=\sqrt[\sqrt2]{ \frac{2}{n_{inputs} + n_{outputs}} } σ=2ninputs+noutputs2 |
非饱和激活函数
ReLU函数并不是完美的。它会出现dying ReLU问题:在训练过程中,一些神经元实际上已经死了,即它们只输出0。在训练过程中,如果神经元的权重更新到神经元输入的总权重是负值时,这个神经元就会开始输出0。当这种情况发生时,除非ReLU函数的梯度为0并且输入为负,否则这个神经元就不会再重新开始工作。
要解决这个问题,可以使用ReLU函数的变种,比如leaky ReLU(带泄露线性整流函数)。这个函数定义为 L e a k y R e L U a ( z ) = m a x ( a z , z ) LeakyReLU_a(z)=max(az, z) LeakyReLUa(z)=max(az,z)。超参数a表示函数“泄露”程度:它是函数中z<0时的坡度,这个小坡度可以保证leaky ReLU不会死,它可以进入一个很长的昏迷其,但最后还是有机会醒过来。
最后,Clevert等人提出了一个新的激活函数,称为ELU(加速线性单元),它的表现优于ReLU的所有变种:训练时间减小,神经网络再测试集的表现也更好。
E L U a ( z ) = { a ( e x p ( z ) − 1 ) z < 0 z z ≥ 0 ELU_a(z)= \begin{cases} a(exp(z)-1) & z<0 \\ z & z \geq 0 \end{cases} ELUa(z)={ a(exp(z)−1)zz<0z≥0
那么在你的深度神经网络的隐藏层到底应该使用哪一种激活函数呢?
使用elu激活函数:hidden1 = fully_connected(X, n_hidden1, activation_fn=tf.nn.elu)
使用leaky ReLU激活函数:
def leaky_relu(z, name=None):
return tf.maximum(0.01 * z, z, name=name)
hidden1 = fully_connected(X, n_hidden1, activation_fn=leaky_relu)
批量归一化
无监督的预训练
辅助任务中的预训练
场景:构建一个人脸识别系统,但是你的样本很少。
方法:在网上收集很多随机的人像图片,用他们可以训练第一个神经网络来检测两张不同的照片是否是同一个人,从而得到一个人脸特征检测器,然后重用这个网络的低层,这样就能用很少的训练数据训练出一个优质的人脸分类器。
快速优化器
学习速率调度
以一个高学习速率开始,然后一旦它停止快速过程就降低速率,那么你会获得一个比最优固定学习速率梗快速的方案。
常用学习计划:
预定分段常数学习速率
性能调度
指数调度:
推荐,因为更容易实现、微调,且收敛到最优解的速度稍快
功率调度
通过正则化避免过度拟合
利用过滤器绘制特征图
import numpy as np
from sklearn.datasets import load_sample_images
import tensorflow as tf
import matplotlib.pyplot as plt
dataset = np.array(load_sample_images().images, dtype=np.float32)
batch_size, height, width, channels = dataset.shape
filters_test = np.zeros(shape=(7, 7, channels, 2), dtype=np.float32)
filters_test[:, 3, :, 0] = 1
filters_test[3, :, :, 1] = 1
X = tf.placeholder(tf.float32, shape=(None, height, width, channels))
convolution = tf.nn.conv2d(X, filters_test, strides=[1, 2, 2, 1], padding="SAME")
with tf.Session() as sess:
output = sess.run(convolution, feed_dict={
X: dataset})
plt.imshow(output[0, :, :, 1])
plt.show()
输出结果
常用的卷积神经网络
TensorFlow中的基本RNN
假设RNN只运行两个时间迭代,每个时间迭代输入一个大小为3的向量。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: simple_rnn.py
@time: 2019/6/15 16:53
@desc: 实现一个最简单的RNN网络。我们将使用tanh激活函数创建一个由5个
神经元组成的一层RNN。假设RNN只运行两个时间迭代,每个时间迭代
输入一个大小为3的向量。
"""
import tensorflow as tf
import numpy as np
n_inputs = 3
n_neurons = 5
x0 = tf.placeholder(tf.float32, [None, n_inputs])
x1 = tf.placeholder(tf.float32, [None, n_inputs])
Wx = tf.Variable(tf.random_normal(shape=[n_inputs, n_neurons], dtype=tf.float32))
Wy = tf.Variable(tf.random_normal(shape=[n_neurons, n_neurons], dtype=tf.float32))
b = tf.Variable(tf.zeros([1, n_neurons], dtype=tf.float32))
y0 = tf.tanh(tf.matmul(x0, Wx) + b)
y1 = tf.tanh(tf.matmul(y0, Wy) + tf.matmul(x1, Wx) + b)
init = tf.global_variables_initializer()
# Mini-batch:包含4个实例的小批次
x0_batch = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 0, 1]]) # t=0
x1_batch = np.array([[9, 8, 7], [0, 0, 0], [6, 5, 4], [3, 2, 1]]) # t=1
with tf.Session() as sess:
init.run()
y0_val, y1_val = sess.run([y0, y1], feed_dict={
x0: x0_batch, x1: x1_batch})
print(y0_val)
print('-'*50)
print(y1_val)
运行结果
通过时间静态展开
static_rnn()函数通过链式单元来创建一个展开的RNN网络。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: simple_rnn2.py
@time: 2019/6/15 17:06
@desc: 与前一个程序相同
"""
import tensorflow as tf
from tensorflow.contrib.rnn import BasicRNNCell
from tensorflow.contrib.rnn import static_rnn
import numpy as np
n_inputs = 3
n_neurons = 5
x0 = tf.placeholder(tf.float32, [None, n_inputs])
x1 = tf.placeholder(tf.float32, [None, n_inputs])
basic_cell = BasicRNNCell(num_units=n_neurons)
output_seqs, states = static_rnn(basic_cell, [x0, x1], dtype=tf.float32)
y0, y1 = output_seqs
init = tf.global_variables_initializer()
# Mini-batch:包含4个实例的小批次
x0_batch = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 0, 1]]) # t=0
x1_batch = np.array([[9, 8, 7], [0, 0, 0], [6, 5, 4], [3, 2, 1]]) # t=1
with tf.Session() as sess:
init.run()
y0_val, y1_val = sess.run([y0, y1], feed_dict={
x0: x0_batch, x1: x1_batch})
print(y0_val)
print('-'*50)
print(y1_val)
运行结果
通过时间动态展开
利用dynamic_rnn()和while_loop()
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: dynamic_rnn1.py
@time: 2019/6/16 13:37
@desc: 通过时间动态展开 dynamic_rnn
"""
import tensorflow as tf
from tensorflow.contrib.rnn import BasicRNNCell
import numpy as np
n_steps = 2
n_inputs = 3
n_neurons = 5
x = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
basic_cell = BasicRNNCell(num_units=n_neurons)
outputs, states = tf.nn.dynamic_rnn(basic_cell, x, dtype=tf.float32)
x_batch = np.array([
[[0, 1, 2], [9, 8, 7]],
[[3, 4, 5], [0, 0, 0]],
[[6, 7, 8], [6, 5, 4]],
[[9, 0, 1], [3, 2, 1]],
])
init = tf.global_variables_initializer()
with tf.Session() as sess:
init.run()
outputs_val = outputs.eval(feed_dict={
x: x_batch})
print(outputs_val)
运行结果
这时问题来了,动态、静态这两种有啥区别呢?
参考:tensor flow dynamic_rnn 与rnn有啥区别?
处理长度可变输入序列
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: dynamic_rnn2.py
@time: 2019/6/17 9:42
@desc: 处理长度可变输入序列
"""
import tensorflow as tf
from tensorflow.contrib.rnn import BasicRNNCell
import numpy as np
n_steps = 2
n_inputs = 3
n_neurons = 5
seq_length = tf.placeholder(tf.int32, [None])
x = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
basic_cell = BasicRNNCell(num_units=n_neurons)
outputs, states = tf.nn.dynamic_rnn(basic_cell, x, dtype=tf.float32, sequence_length=seq_length)
# 假设第二个输出序列仅包含一个输入。为了适应输入张量X,必须使用零向量填充输入。
x_batch = np.array([
[[0, 1, 2], [9, 8, 7]],
[[3, 4, 5], [0, 0, 0]],
[[6, 7, 8], [6, 5, 4]],
[[9, 0, 1], [3, 2, 1]],
])
seq_length_batch = np.array([2, 1, 2, 2])
init = tf.global_variables_initializer()
with tf.Session() as sess:
init.run()
outputs_val, states_val = sess.run([outputs, states], feed_dict={
x: x_batch, seq_length: seq_length_batch})
print(outputs_val)
结果分析
RNN每一次迭代超过输入长度的部分输出零向量。
此外,状态张量包含了每个单元的最终状态(除了零向量)。
处理长度可变输出序列
最通常的解决方案是定义一种被称为**序列结束令牌(EOS token)**的特殊输出。
训练RNN
通过时间反向传播(BPTT):梯度通过被成本函数使用的所有输出向后流动,而不是仅仅通过输出最终输出。
训练序列分类器
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: rnn_test1.py
@time: 2019/6/17 10:28
@desc: 训练一个识别MNIST图像的RNN网络。
"""
import tensorflow as tf
from tensorflow.contrib.layers import fully_connected
from tensorflow.contrib.rnn import BasicRNNCell
from tensorflow.examples.tutorials.mnist import input_data
n_steps = 28
n_inputs = 28
n_neurons = 150
n_outputs = 10
learning_rate = 0.001
n_epochs = 100
batch_size = 150
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.int32, [None])
basic_cell = BasicRNNCell(num_units=n_neurons)
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)
logits = fully_connected(states, n_outputs, activation_fn=None)
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
loss = tf.reduce_mean(xentropy)
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))
init = tf.global_variables_initializer()
# 加载MNIST数据,并按照网格的要求改造测试数据。
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
X_test = mnist.test.images.reshape((-1, n_steps, n_inputs))
y_test = mnist.test.labels
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
for iteration in range(mnist.train.num_examples // batch_size):
X_batch, y_batch = mnist.train.next_batch(batch_size)
X_batch = X_batch.reshape((-1, n_steps, n_inputs))
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
acc_train = accuracy.eval(feed_dict={
X: X_batch, y: y_batch})
acc_test = accuracy.eval(feed_dict={
X: X_test, y: y_test})
print(epoch, "Train accuracy: ", acc_train, "Test accuracy: ", acc_test)
运行结果
tf.nn.in_top_k:主要是用于计算预测的结果和实际结果的是否相等,返回一个bool类型的张量,tf.nn.in_top_k(prediction, target, K):prediction就是表示你预测的结果,大小就是预测样本的数量乘以输出的维度,类型是tf.float32等。target就是实际样本类别的索引,大小就是样本数量的个数。K表示每个样本的预测结果的前K个最大的数里面是否含有target中的值。一般都是取1。
参考链接:tf.nn.in_top_k的用法
tf.cast:将x的数据格式转化成dtype。例如,原来x的数据格式是bool,那么将其转化成float以后,就能够将其转化成0和1的序列。反之也可以。
cast(
x,
dtype,
name=None
)
训练预测时间序列
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: rnn_test2.py
@time: 2019/6/18 10:11
@desc: 训练预测时间序列
"""
import tensorflow as tf
import numpy as np
from tensorflow.contrib.layers import fully_connected
from tensorflow.contrib.rnn import BasicRNNCell
from tensorflow.contrib.rnn import OutputProjectionWrapper
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
n_steps = 100
n_inputs = 1
n_neurous = 100
n_outputs = 1
learning_rate = 0.001
n_iterations = 10000
batch_size = 50
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_steps, n_outputs])
# 现在在每个时间迭代,有一个大小为100的输出向量,但是实际上我们需要一个单独的输出值。
# 最简单的解决方案是将单元格包装在OutputProjectionWrapper中。
# cell = OutputProjectionWrapper(BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu), output_size=n_outputs)
# 用技巧提高速度
cell = BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu)
rnn_outputs, states = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
stacked_rnn_outputs = tf.reshape(rnn_outputs, [-1, n_neurous])
stacked_outputs = fully_connected(stacked_rnn_outputs, n_outputs, activation_fn=None)
outputs = tf.reshape(stacked_outputs, [-1, n_steps, n_outputs])
loss = tf.reduce_mean(tf.square(outputs - y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
X_data = np.linspace(0, 15, 101)
with tf.Session() as sess:
init.run()
for iteration in range(n_iterations):
X_batch = X_data[:-1][np.newaxis, :, np.newaxis]
y_batch = X_batch * np.sin(X_batch) / 3 + 2 * np.sin(5 * X_batch)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
if iteration % 100 == 0:
mse = loss.eval(feed_dict={
X: X_batch, y: y_batch})
print(iteration, "\tMSE", mse)
X_new = X_data[1:][np.newaxis, :, np.newaxis]
y_true = X_new * np.sin(X_new) / 3 + 2 * np.sin(5 * X_new)
y_pred = sess.run(outputs, feed_dict={
X: X_new})
print(X_new.flatten())
print('真实结果:', y_true.flatten())
print('预测结果:', y_pred.flatten())
fig = plt.figure(dpi=150)
plt.plot(X_new.flatten(), y_true.flatten(), 'r', label='y_true')
plt.plot(X_new.flatten(), y_pred.flatten(), 'b', label='y_pred')
plt.legend()
plt.show()
创造性的RNN
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: rnn_test3.py
@time: 2019/6/19 8:47
@desc: 创造性RNN
"""
import tensorflow as tf
import numpy as np
from tensorflow.contrib.layers import fully_connected
from tensorflow.contrib.rnn import BasicRNNCell
from tensorflow.contrib.rnn import OutputProjectionWrapper
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
n_steps = 20
n_inputs = 1
n_neurous = 100
n_outputs = 1
learning_rate = 0.001
n_iterations = 10000
batch_size = 50
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_steps, n_outputs])
# 现在在每个时间迭代,有一个大小为100的输出向量,但是实际上我们需要一个单独的输出值。
# 最简单的解决方案是将单元格包装在OutputProjectionWrapper中。
# cell = OutputProjectionWrapper(BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu), output_size=n_outputs)
# 用技巧提高速度
cell = BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu)
rnn_outputs, states = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
stacked_rnn_outputs = tf.reshape(rnn_outputs, [-1, n_neurous])
stacked_outputs = fully_connected(stacked_rnn_outputs, n_outputs, activation_fn=None)
outputs = tf.reshape(stacked_outputs, [-1, n_steps, n_outputs])
loss = tf.reduce_mean(tf.square(outputs - y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
X_data = np.linspace(0, 19, 20)
with tf.Session() as sess:
init.run()
for iteration in range(n_iterations):
X_batch = X_data[np.newaxis, :, np.newaxis]
y_batch = X_batch * np.sin(X_batch) / 3 + 2 * np.sin(5 * X_batch)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
if iteration % 100 == 0:
mse = loss.eval(feed_dict={
X: X_batch, y: y_batch})
print(iteration, "\tMSE", mse)
# sequence = [0.] * n_steps
sequence = list(y_batch.flatten())
for iteration in range(300):
XX_batch = np.array(sequence[-n_steps:]).reshape(1, n_steps, 1)
y_pred = sess.run(outputs, feed_dict={
X: XX_batch})
sequence.append(y_pred[0, -1, 0])
fig = plt.figure(dpi=150)
plt.plot(sequence)
plt.legend()
plt.show()
深层RNN
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: rnn_test4.py
@time: 2019/6/19 10:10
@desc: 深层RNN
"""
import tensorflow as tf
import numpy as np
from tensorflow.contrib.layers import fully_connected
from tensorflow.contrib.rnn import BasicRNNCell
from tensorflow.contrib.rnn import MultiRNNCell
from tensorflow.contrib.rnn import OutputProjectionWrapper
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
n_steps = 100
n_inputs = 1
n_neurous = 100
n_outputs = 1
n_layers = 10
learning_rate = 0.00001
n_iterations = 10000
batch_size = 50
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_steps, n_outputs])
# 现在在每个时间迭代,有一个大小为100的输出向量,但是实际上我们需要一个单独的输出值。
# 最简单的解决方案是将单元格包装在OutputProjectionWrapper中。
# cell = OutputProjectionWrapper(BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu), output_size=n_outputs)
# 用技巧提高速度
# cell = BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu)
# multi_layer_cell = MultiRNNCell([cell] * n_layers)
layers = [BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu) for _ in range(n_layers)]
multi_layer_cell = MultiRNNCell(layers)
rnn_outputs, states = tf.nn.dynamic_rnn(multi_layer_cell, X, dtype=tf.float32)
stacked_rnn_outputs = tf.reshape(rnn_outputs, [-1, n_neurous])
stacked_outputs = fully_connected(stacked_rnn_outputs, n_outputs, activation_fn=None)
outputs = tf.reshape(stacked_outputs, [-1, n_steps, n_outputs])
loss = tf.reduce_mean(tf.square(outputs - y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
X_data = np.linspace(0, 15, 101)
with tf.Session() as sess:
init.run()
for iteration in range(n_iterations):
X_batch = X_data[:-1][np.newaxis, :, np.newaxis]
y_batch = X_batch * np.sin(X_batch) / 3 + 2 * np.sin(5 * X_batch)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
if iteration % 100 == 0:
mse = loss.eval(feed_dict={
X: X_batch, y: y_batch})
print(iteration, "\tMSE", mse)
X_new = X_data[1:][np.newaxis, :, np.newaxis]
y_true = X_new * np.sin(X_new) / 3 + 2 * np.sin(5 * X_new)
y_pred = sess.run(outputs, feed_dict={
X: X_new})
print(X_new.flatten())
print('真实结果:', y_true.flatten())
print('预测结果:', y_pred.flatten())
plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
fig = plt.figure(dpi=150)
plt.plot(X_new.flatten(), y_true.flatten(), 'r', label='y_true')
plt.plot(X_new.flatten(), y_pred.flatten(), 'b', label='y_pred')
plt.title('深层RNN预测')
plt.legend()
plt.show()
在多个GPU中分配一个深层RNN
并没有多个GPU,所以只是整理了一下代码。。。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: rnn_gpu.py
@time: 2019/6/19 12:11
@desc: 在多个GPU中分配一个深层RNN
"""
import tensorflow as tf
import numpy as np
from tensorflow.contrib.rnn import RNNCell
from tensorflow.contrib.rnn import BasicRNNCell
from tensorflow.contrib.rnn import MultiRNNCell
from tensorflow.contrib.layers import fully_connected
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
class DeviceCellWrapper(RNNCell):
def __init__(self, device, cell):
self._cell = cell
self._device = device
@property
def state_size(self):
return self._cell.state_size
@property
def output(self):
return self._cell.output_size
def __call__(self, inputs, state, scope=None):
with tf.device(self._device):
return self._cell(inputs, state, scope)
n_steps = 100
n_inputs = 1
n_neurous = 100
n_outputs = 1
n_layers = 10
learning_rate = 0.00001
n_iterations = 10000
batch_size = 50
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_steps, n_outputs])
devices = ['/gpu:0', '/gpu:1', '/gpu:2']
cells = [DeviceCellWrapper(dev, BasicRNNCell(num_units=n_neurous)) for dev in devices]
multi_layer_cell = MultiRNNCell(cells)
rnn_outputs, states = tf.nn.dynamic_rnn(multi_layer_cell, X, dtype=tf.float32)
stacked_rnn_outputs = tf.reshape(rnn_outputs, [-1, n_neurous])
stacked_outputs = fully_connected(stacked_rnn_outputs, n_outputs, activation_fn=None)
outputs = tf.reshape(stacked_outputs, [-1, n_steps, n_outputs])
loss = tf.reduce_mean(tf.square(outputs - y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
X_data = np.linspace(0, 15, 101)
with tf.Session() as sess:
init.run()
for iteration in range(n_iterations):
X_batch = X_data[:-1][np.newaxis, :, np.newaxis]
y_batch = X_batch * np.sin(X_batch) / 3 + 2 * np.sin(5 * X_batch)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
if iteration % 100 == 0:
mse = loss.eval(feed_dict={
X: X_batch, y: y_batch})
print(iteration, "\tMSE", mse)
X_new = X_data[1:][np.newaxis, :, np.newaxis]
y_true = X_new * np.sin(X_new) / 3 + 2 * np.sin(5 * X_new)
y_pred = sess.run(outputs, feed_dict={
X: X_new})
print(X_new.flatten())
print('真实结果:', y_true.flatten())
print('预测结果:', y_pred.flatten())
plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
fig = plt.figure(dpi=150)
plt.plot(X_new.flatten(), y_true.flatten(), 'r', label='y_true')
plt.plot(X_new.flatten(), y_pred.flatten(), 'b', label='y_pred')
plt.title('深层RNN预测')
plt.legend()
plt.show()
应用丢弃机制
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: rnn_test5.py
@time: 2019/6/19 13:44
@desc: 应用丢弃机制
"""
import tensorflow as tf
import sys
import numpy as np
from tensorflow.contrib.layers import fully_connected
from tensorflow.contrib.rnn import BasicRNNCell
from tensorflow.contrib.rnn import MultiRNNCell
from tensorflow.contrib.rnn import OutputProjectionWrapper
from tensorflow.contrib.rnn import DropoutWrapper
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
is_training = (sys.argv[-1] == "train")
keep_prob = 0.5
n_steps = 100
n_inputs = 1
n_neurous = 100
n_outputs = 1
n_layers = 10
learning_rate = 0.00001
n_iterations = 10000
batch_size = 50
def make_rnn_cell():
return BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu)
def make_drop_cell():
return DropoutWrapper(make_rnn_cell(), input_keep_prob=keep_prob)
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_steps, n_outputs])
# 现在在每个时间迭代,有一个大小为100的输出向量,但是实际上我们需要一个单独的输出值。
# 最简单的解决方案是将单元格包装在OutputProjectionWrapper中。
# cell = OutputProjectionWrapper(BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu), output_size=n_outputs)
# 用技巧提高速度
layers = [make_rnn_cell() for _ in range(n_layers)]
if is_training:
layers = [make_drop_cell() for _ in range(n_layers)]
multi_layer_cell = MultiRNNCell(layers)
rnn_outputs, states = tf.nn.dynamic_rnn(multi_layer_cell, X, dtype=tf.float32)
stacked_rnn_outputs = tf.reshape(rnn_outputs, [-1, n_neurous])
stacked_outputs = fully_connected(stacked_rnn_outputs, n_outputs, activation_fn=None)
outputs = tf.reshape(stacked_outputs, [-1, n_steps, n_outputs])
loss = tf.reduce_mean(tf.square(outputs - y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
X_data = np.linspace(0, 15, 101)
'''
# 应用丢弃机制
saver = tf.train.Saver()
with tf.Session() as sess:
if is_training:
init.run()
for iteration in range(n_iterations):
# train the model
save_path = saver.save(sess, "./my_model.ckpt")
else:
saver.restore(sess, "./my_model.ckpt")
# use the model
'''
with tf.Session() as sess:
init.run()
for iteration in range(n_iterations):
X_batch = X_data[:-1][np.newaxis, :, np.newaxis]
y_batch = X_batch * np.sin(X_batch) / 3 + 2 * np.sin(5 * X_batch)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
if iteration % 100 == 0:
mse = loss.eval(feed_dict={
X: X_batch, y: y_batch})
print(iteration, "\tMSE", mse)
X_new = X_data[1:][np.newaxis, :, np.newaxis]
y_true = X_new * np.sin(X_new) / 3 + 2 * np.sin(5 * X_new)
y_pred = sess.run(outputs, feed_dict={
X: X_new})
print(X_new.flatten())
print('真实结果:', y_true.flatten())
print('预测结果:', y_pred.flatten())
plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
fig = plt.figure(dpi=150)
plt.plot(X_new.flatten(), y_true.flatten(), 'r', label='y_true')
plt.plot(X_new.flatten(), y_pred.flatten(), 'b', label='y_pred')
plt.title('深层RNN预测')
plt.legend()
plt.show()
LSTM单元
四个不同的全连接层:主层:tanh,直接输出 y t 和 h t y_t和h_t yt和ht;忘记门限:logitstic,控制着哪些长期状态应该被丢弃;输入门限:控制着主层的哪些部分会被加入到长期状态(这就是“部分存储”的原因);输出门限:控制着哪些长期状态应该在这个时间迭代被读取和输出( h t 和 y t h_t和y_t ht和yt)。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: lstm_test1.py
@time: 2019/6/19 14:51
@desc: LSTM单元
"""
import tensorflow as tf
import sys
import numpy as np
from tensorflow.contrib.layers import fully_connected
from tensorflow.contrib.rnn import BasicLSTMCell
from tensorflow.contrib.rnn import MultiRNNCell
from tensorflow.contrib.rnn import OutputProjectionWrapper
from tensorflow.contrib.rnn import DropoutWrapper
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
is_training = (sys.argv[-1] == "train")
keep_prob = 0.5
n_steps = 100
n_inputs = 1
n_neurous = 100
n_outputs = 1
n_layers = 5
learning_rate = 0.00001
n_iterations = 10000
batch_size = 50
def make_rnn_cell():
return BasicLSTMCell(num_units=n_neurous, activation=tf.nn.relu)
def make_drop_cell():
return DropoutWrapper(make_rnn_cell(), input_keep_prob=keep_prob)
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_steps, n_outputs])
# 现在在每个时间迭代,有一个大小为100的输出向量,但是实际上我们需要一个单独的输出值。
# 最简单的解决方案是将单元格包装在OutputProjectionWrapper中。
# cell = OutputProjectionWrapper(BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu), output_size=n_outputs)
# 用技巧提高速度
layers = [make_rnn_cell() for _ in range(n_layers)]
# if is_training:
# layers = [make_drop_cell() for _ in range(n_layers)]
multi_layer_cell = MultiRNNCell(layers)
rnn_outputs, states = tf.nn.dynamic_rnn(multi_layer_cell, X, dtype=tf.float32)
stacked_rnn_outputs = tf.reshape(rnn_outputs, [-1, n_neurous])
stacked_outputs = fully_connected(stacked_rnn_outputs, n_outputs, activation_fn=None)
outputs = tf.reshape(stacked_outputs, [-1, n_steps, n_outputs])
loss = tf.reduce_mean(tf.square(outputs - y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
X_data = np.linspace(0, 15, 101)
with tf.Session() as sess:
init.run()
for iteration in range(n_iterations):
X_batch = X_data[:-1][np.newaxis, :, np.newaxis]
y_batch = X_batch * np.sin(X_batch) / 3 + 2 * np.sin(5 * X_batch)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
if iteration % 100 == 0:
mse = loss.eval(feed_dict={
X: X_batch, y: y_batch})
print(iteration, "\tMSE", mse)
X_new = X_data[1:][np.newaxis, :, np.newaxis]
y_true = X_new * np.sin(X_new) / 3 + 2 * np.sin(5 * X_new)
y_pred = sess.run(outputs, feed_dict={
X: X_new})
print(X_new.flatten())
print('真实结果:', y_true.flatten())
print('预测结果:', y_pred.flatten())
plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
fig = plt.figure(dpi=150)
plt.plot(X_new.flatten(), y_true.flatten(), 'r', label='y_true')
plt.plot(X_new.flatten(), y_pred.flatten(), 'b', label='y_pred')
plt.title('深层RNN预测')
plt.legend()
plt.show()
运行结果
窥视孔连接
窥视孔连接(peephole connections):LSTM变体,当前一个长期状态 c ( t − 1 ) c_{(t-1)} c(t−1)作为输入传入忘记门限和输入门限,当前的长期状态 c ( t ) c_{(t)} c(t)作为输入传出门限控制器。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: lstm_test2.py
@time: 2019/6/19 16:36
@desc: 窥视孔连接
"""
import tensorflow as tf
import numpy as np
from tensorflow.contrib.layers import fully_connected
from tensorflow.contrib.rnn import LSTMCell
from tensorflow.contrib.rnn import MultiRNNCell
from tensorflow.contrib.rnn import OutputProjectionWrapper
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
n_steps = 100
n_inputs = 1
n_neurous = 100
n_outputs = 1
n_layers = 10
learning_rate = 0.00001
n_iterations = 10000
batch_size = 50
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_steps, n_outputs])
# 现在在每个时间迭代,有一个大小为100的输出向量,但是实际上我们需要一个单独的输出值。
# 最简单的解决方案是将单元格包装在OutputProjectionWrapper中。
# cell = OutputProjectionWrapper(BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu), output_size=n_outputs)
# 用技巧提高速度
# cell = BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu)
# multi_layer_cell = MultiRNNCell([cell] * n_layers)
layers = [LSTMCell(num_units=n_neurous, activation=tf.nn.relu, use_peepholes=True) for _ in range(n_layers)]
multi_layer_cell = MultiRNNCell(layers)
rnn_outputs, states = tf.nn.dynamic_rnn(multi_layer_cell, X, dtype=tf.float32)
stacked_rnn_outputs = tf.reshape(rnn_outputs, [-1, n_neurous])
stacked_outputs = fully_connected(stacked_rnn_outputs, n_outputs, activation_fn=None)
outputs = tf.reshape(stacked_outputs, [-1, n_steps, n_outputs])
loss = tf.reduce_mean(tf.square(outputs - y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
X_data = np.linspace(0, 15, 101)
with tf.Session() as sess:
init.run()
for iteration in range(n_iterations):
X_batch = X_data[:-1][np.newaxis, :, np.newaxis]
y_batch = X_batch * np.sin(X_batch) / 3 + 2 * np.sin(5 * X_batch)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
if iteration % 100 == 0:
mse = loss.eval(feed_dict={
X: X_batch, y: y_batch})
print(iteration, "\tMSE", mse)
X_new = X_data[1:][np.newaxis, :, np.newaxis]
y_true = X_new * np.sin(X_new) / 3 + 2 * np.sin(5 * X_new)
y_pred = sess.run(outputs, feed_dict={
X: X_new})
print(X_new.flatten())
print('真实结果:', y_true.flatten())
print('预测结果:', y_pred.flatten())
plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
fig = plt.figure(dpi=150)
plt.plot(X_new.flatten(), y_true.flatten(), 'r', label='y_true')
plt.plot(X_new.flatten(), y_pred.flatten(), 'b', label='y_pred')
plt.title('深层RNN预测')
plt.legend()
plt.show()
运行结果
GRU单元
GRU单元是LSTM的简化版本,其主要简化了:
GRU 是新一代的循环神经网络,与 LSTM 非常相似。与 LSTM 相比,GRU 去除掉了细胞状态,使用隐藏状态来进行信息的传递。它只包含两个门:更新门和重置门。
更新门:更新门的作用类似于 LSTM 中的遗忘门和输入门。它决定了要忘记哪些信息以及哪些新信息需要被添加。
重置门:重置门用于决定遗忘先前信息的程度。
GRU 的张量运算较少,因此它比 LSTM 的训练更快一下。很难去判定这两者到底谁更好,研究人员通常会两者都试一下,然后选择最合适的。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: gru_test1.py
@time: 2019/6/19 17:07
@desc: GRU单元
"""
import tensorflow as tf
import numpy as np
from tensorflow.contrib.layers import fully_connected
from tensorflow.contrib.rnn import GRUCell
from tensorflow.contrib.rnn import MultiRNNCell
from tensorflow.contrib.rnn import OutputProjectionWrapper
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
n_steps = 100
n_inputs = 1
n_neurous = 100
n_outputs = 1
n_layers = 10
learning_rate = 0.00001
n_iterations = 10000
batch_size = 50
X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_steps, n_outputs])
# 现在在每个时间迭代,有一个大小为100的输出向量,但是实际上我们需要一个单独的输出值。
# 最简单的解决方案是将单元格包装在OutputProjectionWrapper中。
# cell = OutputProjectionWrapper(BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu), output_size=n_outputs)
# 用技巧提高速度
# cell = BasicRNNCell(num_units=n_neurous, activation=tf.nn.relu)
# multi_layer_cell = MultiRNNCell([cell] * n_layers)
layers = [GRUCell(num_units=n_neurous, activation=tf.nn.relu) for _ in range(n_layers)]
multi_layer_cell = MultiRNNCell(layers)
rnn_outputs, states = tf.nn.dynamic_rnn(multi_layer_cell, X, dtype=tf.float32)
stacked_rnn_outputs = tf.reshape(rnn_outputs, [-1, n_neurous])
stacked_outputs = fully_connected(stacked_rnn_outputs, n_outputs, activation_fn=None)
outputs = tf.reshape(stacked_outputs, [-1, n_steps, n_outputs])
loss = tf.reduce_mean(tf.square(outputs - y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
X_data = np.linspace(0, 15, 101)
with tf.Session() as sess:
init.run()
for iteration in range(n_iterations):
X_batch = X_data[:-1][np.newaxis, :, np.newaxis]
y_batch = X_batch * np.sin(X_batch) / 3 + 2 * np.sin(5 * X_batch)
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
if iteration % 100 == 0:
mse = loss.eval(feed_dict={
X: X_batch, y: y_batch})
print(iteration, "\tMSE", mse)
X_new = X_data[1:][np.newaxis, :, np.newaxis]
y_true = X_new * np.sin(X_new) / 3 + 2 * np.sin(5 * X_new)
y_pred = sess.run(outputs, feed_dict={
X: X_new})
print(X_new.flatten())
print('真实结果:', y_true.flatten())
print('预测结果:', y_pred.flatten())
plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
fig = plt.figure(dpi=150)
plt.plot(X_new.flatten(), y_true.flatten(), 'r', label='y_true')
plt.plot(X_new.flatten(), y_pred.flatten(), 'b', label='y_pred')
plt.title('GRU预测')
plt.legend()
plt.show()
参考链接:深入理解LSTM,窥视孔连接,GRU
更好的参考链接:GRU与LSTM总结
其他变形的LSTM网络总结
参考链接:直观理解LSTM(长短时记忆网络)
部分课后题的摘抄
在构建RNN时使用dynamic_rnn()而不是static_rnn()的优势是什么?
如何处理变长输入序列?变长输出序列又会怎么样?
为了处理可变长度的输入序列,最简单的方法是在调用static_rnn()或dynamic_rnn()方法时传入sequence_length参数。另一个方法是填充长度较小的输入(比如,用0填充)来使其与最大输入长度相同(这可能比第一种方法快,因为所有输入序列具有相同的长度)。
为了处理可变长度的输出序列,如果事先知道每个输出序列的长度,就可以使用sequence_length参数(例如,序列到序列RNN使用暴力评分标记视频中的每一帧:输出序列和输入序列长度完全一致)。如果事先不知道输出序列的长度,则可以使用填充方法:始终输出相同大小的序列,但是忽略end-of-sequence标记之后的任何输出(在计算成本函数时忽略它们)。
在多个GPU之间分配训练和执行层次RNN的常见方式是什么?
为了在多个GPU直接分配训练并执行深度RNN,一个常用的简单技术是将每个层放在不同的GPU上。
使用不完整的线性自动编码器实现PCA
一个自动编码器由两部分组成:编码器(或称为识别网络),解码器(或称为生成网络)。
输出层的神经元数量必须等于输入层的数量。
输出通常被称为重建,因为自动编码器尝试重建输入,并且成本函数包含重建损失,当重建与输入不同时,该损失会惩罚模型。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_1.py
@time: 2019/6/20 14:31
@desc: 使用不完整的线性自动编码器实现PCA
"""
import tensorflow as tf
from tensorflow.contrib.layers import fully_connected
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
# 生成数据
import numpy.random as rnd
rnd.seed(4)
m = 200
w1, w2 = 0.1, 0.3
noise = 0.1
angles = rnd.rand(m) * 3 * np.pi / 2 - 0.5
data = np.empty((m, 3))
data[:, 0] = np.cos(angles) + np.sin(angles) / 2 + noise * rnd.randn(m) / 2
data[:, 1] = np.sin(angles) * 0.7 + noise * rnd.randn(m) / 2
data[:, 2] = data[:, 0] * w1 + data[:, 1] * w2 + noise * rnd.randn(m)
# 数据标准化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(data[:100])
X_test = scaler.transform(data[100:])
# 3D inputs
n_inputs = 3
# 2D codings
n_hidden = 2
n_outputs = n_inputs
learning_rate = 0.01
n_iterations = 1000
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
hidden = fully_connected(X, n_hidden, activation_fn=None)
outputs = fully_connected(hidden, n_outputs, activation_fn=None)
# MSE
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X))
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(reconstruction_loss)
init = tf.global_variables_initializer()
codings = hidden
with tf.Session() as sess:
init.run()
for n_iterations in range(n_iterations):
training_op.run(feed_dict={
X: X_train})
codings_val = codings.eval(feed_dict={
X: X_test})
fig = plt.figure(figsize=(4, 3), dpi=300)
plt.plot(codings_val[:, 0], codings_val[:, 1], "b.")
plt.xlabel("$z_1$", fontsize=18)
plt.ylabel("$z_2$", fontsize=18, rotation=0)
plt.show()
运行结果
画出的是中间两个神经元隐藏层。(降维)
栈式自动编码器(深度自动编码器)
使用He初始化,ELU激活函数,以及 l 2 l_2 l2正则化构建了一个MNIST栈式自动编码器。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_2.py
@time: 2019/6/21 8:48
@desc: 使用He初始化,ELU激活函数,以及$l_2$正则化构建了一个MNIST栈式自动编码器
"""
import tensorflow as tf
from tensorflow.contrib.layers import fully_connected
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import sys
n_inputs = 28 * 28 # for MNIST
n_hidden1 = 300
n_hidden2 = 150 # codings
n_hidden3 = n_hidden1
n_outputs = n_inputs
learning_rate = 0.001
l2_reg = 0.001
n_epochs = 30
batch_size = 150
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
with tf.contrib.framework.arg_scope(
[fully_connected],
activation_fn=tf.nn.elu,
weights_initializer=tf.contrib.layers.variance_scaling_initializer(),
weights_regularizer=tf.contrib.layers.l2_regularizer(l2_reg)
):
hidden1 = fully_connected(X, n_hidden1)
hidden2 = fully_connected(hidden1, n_hidden2) # codings
hidden3 = fully_connected(hidden2, n_hidden3)
outputs = fully_connected(hidden3, n_outputs, activation_fn=None)
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X)) # MSE
reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
loss = tf.add_n([reconstruction_loss] + reg_losses)
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
init.run()
for epoch in range(n_epochs):
n_batches = mnist.train.num_examples // batch_size
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
X_batch, y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op, feed_dict={
X: X_batch})
loss_train = reconstruction_loss.eval(feed_dict={
X: X_batch})
print("\r{}".format(epoch), "Train MSE:", loss_train)
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_all_layers.ckpt")
# 可视化
def plot_image(image, shape=[28, 28]):
plt.imshow(image.reshape(shape), cmap="Greys", interpolation="nearest")
plt.axis("off")
def show_reconstructed_digits(X, outputs, model_path = None, n_test_digits = 2):
with tf.Session() as sess:
if model_path:
saver.restore(sess, model_path)
X_test = mnist.test.images[:n_test_digits]
outputs_val = outputs.eval(feed_dict={
X: X_test})
fig = plt.figure(figsize=(8, 3 * n_test_digits))
for digit_index in range(n_test_digits):
plt.subplot(n_test_digits, 2, digit_index * 2 + 1)
plot_image(X_test[digit_index])
plt.subplot(n_test_digits, 2, digit_index * 2 + 2)
plot_image(outputs_val[digit_index])
plt.show()
show_reconstructed_digits(X, outputs, "D:/Python3Space/BookStudy/book4/model/my_model_all_layers.ckpt")
运行结果
权重绑定
将解码层的权重和编码层的权重联系起来。 W N − L + 1 = W L T W_{N-L+1} = W_L^T WN−L+1=WLT
weight3和weights4不是变量,它们分别是weights2和weights1的转置(它们被“绑定”在一起)。
因为它们不是变量,所以没有必要进行正则化,只正则化weights1和weigts2。
偏置项从来不会被绑定,也不会被正则化。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_3.py
@time: 2019/6/21 9:48
@desc: 权重绑定
"""
import tensorflow as tf
from tensorflow.contrib.layers import fully_connected
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import sys
# 在复制一遍可视化代码
def plot_image(image, shape=[28, 28]):
plt.imshow(image.reshape(shape), cmap="Greys", interpolation="nearest")
plt.axis("off")
def show_reconstructed_digits(X, outputs, model_path = None, n_test_digits = 2):
with tf.Session() as sess:
if model_path:
saver.restore(sess, model_path)
X_test = mnist.test.images[:n_test_digits]
outputs_val = outputs.eval(feed_dict={
X: X_test})
fig = plt.figure(figsize=(8, 3 * n_test_digits))
for digit_index in range(n_test_digits):
plt.subplot(n_test_digits, 2, digit_index * 2 + 1)
plot_image(X_test[digit_index])
plt.subplot(n_test_digits, 2, digit_index * 2 + 2)
plot_image(outputs_val[digit_index])
plt.show()
n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 150 # codings
n_hidden3 = n_hidden1
n_outputs = n_inputs
learning_rate = 0.01
l2_reg = 0.001
n_epochs = 5
batch_size = 150
activation = tf.nn.elu
regularizer = tf.contrib.layers.l2_regularizer(l2_reg)
initializer = tf.contrib.layers.variance_scaling_initializer()
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
weights1_init = initializer([n_inputs, n_hidden1])
weights2_init = initializer([n_hidden1, n_hidden2])
weights1 = tf.Variable(weights1_init, dtype=tf.float32, name='weights1')
weights2 = tf.Variable(weights2_init, dtype=tf.float32, name='weights2')
weights3 = tf.transpose(weights2, name='weights3') # 权重绑定
weights4 = tf.transpose(weights1, name='weights4') # 权重绑定
biases1 = tf.Variable(tf.zeros(n_hidden1), name='biases1')
biases2 = tf.Variable(tf.zeros(n_hidden2), name='biases2')
biases3 = tf.Variable(tf.zeros(n_hidden3), name='biases3')
biases4 = tf.Variable(tf.zeros(n_outputs), name='biases4')
hidden1 = activation(tf.matmul(X, weights1) + biases1)
hidden2 = activation(tf.matmul(hidden1, weights2) + biases2)
hidden3 = activation(tf.matmul(hidden2, weights3) + biases3)
outputs = tf.matmul(hidden3, weights4) + biases4
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X))
reg_loss = regularizer(weights1) + regularizer(weights2)
loss = reconstruction_loss + reg_loss
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
init.run()
for epoch in range(n_epochs):
n_batches = mnist.train.num_examples // batch_size
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
X_batch, y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op, feed_dict={
X: X_batch})
loss_train = reconstruction_loss.eval(feed_dict={
X: X_batch})
print("\r{}".format(epoch), "Train MSE:", loss_train)
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_tying_weights.ckpt")
show_reconstructed_digits(X, outputs, "D:/Python3Space/BookStudy/book4/model/my_model_tying_weights.ckpt")
运行结果
在多个图中一次训练一个自动编码器
有许多方法可以一次训练一个自动编码器。第一种方法是使用不同的图形对每个自动编码器进行训练,然后我们通过简单地初始化它和从这些自动编码器复制的权重和偏差来创建堆叠的自动编码器。
让我们创建一个函数来训练一个自动编码器并返回转换后的训练集(即隐藏层的输出)和模型参数。
现在让我们训练两个自动编码器。第一个是关于训练数据的训练,第二个是关于上一个自动编码器隐藏层输出的训练。
最后,通过简单地重用我们刚刚训练的自动编码器的权重和偏差,我们可以创建一个堆叠的自动编码器。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_6.py
@time: 2019/6/24 10:29
@desc: 在多个图中一次训练一个自动编码器
"""
import tensorflow as tf
from tensorflow.contrib.layers import fully_connected
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import sys
import numpy.random as rnd
from functools import partial
# 在复制一遍可视化代码
def plot_image(image, shape=[28, 28]):
plt.imshow(image.reshape(shape), cmap="Greys", interpolation="nearest")
plt.axis("off")
def show_reconstructed_digits(X, outputs, n_test_digits=2):
with tf.Session() as sess:
# if model_path:
# saver.restore(sess, model_path)
X_test = mnist.test.images[:n_test_digits]
outputs_val = outputs.eval(feed_dict={
X: X_test})
fig = plt.figure(figsize=(8, 3 * n_test_digits))
for digit_index in range(n_test_digits):
plt.subplot(n_test_digits, 2, digit_index * 2 + 1)
plot_image(X_test[digit_index])
plt.subplot(n_test_digits, 2, digit_index * 2 + 2)
plot_image(outputs_val[digit_index])
plt.show()
# 有许多方法可以一次训练一个自动编码器。第一种方法是使用不同的图形对每个自动编码器进行训练,
# 然后我们通过简单地初始化它和从这些自动编码器复制的权重和偏差来创建堆叠的自动编码器。
def train_autoencoder(X_train, n_neurons, n_epochs, batch_size,
learning_rate=0.01, l2_reg=0.0005,
activation=tf.nn.elu, seed=42):
graph = tf.Graph()
with graph.as_default():
tf.set_random_seed(seed)
n_inputs = X_train.shape[1]
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
my_dense_layer = partial(
tf.layers.dense,
activation=activation,
kernel_initializer=tf.contrib.layers.variance_scaling_initializer(),
kernel_regularizer=tf.contrib.layers.l2_regularizer(l2_reg))
hidden = my_dense_layer(X, n_neurons, name="hidden")
outputs = my_dense_layer(hidden, n_inputs, activation=None, name="outputs")
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X))
reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
loss = tf.add_n([reconstruction_loss] + reg_losses)
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
with tf.Session(graph=graph) as sess:
init.run()
for epoch in range(n_epochs):
n_batches = len(X_train) // batch_size
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
indices = rnd.permutation(len(X_train))[:batch_size]
X_batch = X_train[indices]
sess.run(training_op, feed_dict={
X: X_batch})
loss_train = reconstruction_loss.eval(feed_dict={
X: X_batch})
print("\r{}".format(epoch), "Train MSE:", loss_train)
params = dict([(var.name, var.eval()) for var in tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)])
hidden_val = hidden.eval(feed_dict={
X: X_train})
return hidden_val, params["hidden/kernel:0"], params["hidden/bias:0"], params["outputs/kernel:0"], params["outputs/bias:0"]
mnist = input_data.read_data_sets('F:/JupyterWorkspace/MNIST_data/')
hidden_output, W1, b1, W4, b4 = train_autoencoder(mnist.train.images, n_neurons=300, n_epochs=4, batch_size=150)
_, W2, b2, W3, b3 = train_autoencoder(hidden_output, n_neurons=150, n_epochs=4, batch_size=150)
# 最后,通过简单地重用我们刚刚训练的自动编码器的权重和偏差,我们可以创建一个堆叠的自动编码器。
n_inputs = 28*28
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
hidden1 = tf.nn.elu(tf.matmul(X, W1) + b1)
hidden2 = tf.nn.elu(tf.matmul(hidden1, W2) + b2)
hidden3 = tf.nn.elu(tf.matmul(hidden2, W3) + b3)
outputs = tf.matmul(hidden3, W4) + b4
show_reconstructed_digits(X, outputs)
运行结果
在一个图中一次训练一个自动编码器
另一种方法是使用单个图。为此,我们为完整的堆叠式自动编码器创建了图形,但是我们还添加了独立训练每个自动编码器的操作:第一阶段训练底层和顶层(即。第一个自动编码器和第二阶段训练两个中间层(即。第二个自动编码器)。
通过设置optimizer.minimize
参数中的var_list
,达到freeze其他权重的目的。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_7.py
@time: 2019/7/2 9:02
@desc: 在一个图中一次训练一个自动编码器
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import sys
import numpy.random as rnd
from functools import partial
n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 150 # codings
n_hidden3 = n_hidden1
n_outputs = n_inputs
learning_rate = 0.01
l2_reg = 0.0001
activation = tf.nn.elu
regularizer = tf.contrib.layers.l2_regularizer(l2_reg)
initializer = tf.contrib.layers.variance_scaling_initializer()
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
weights1_init = initializer([n_inputs, n_hidden1])
weights2_init = initializer([n_hidden1, n_hidden2])
weights3_init = initializer([n_hidden2, n_hidden3])
weights4_init = initializer([n_hidden3, n_outputs])
weights1 = tf.Variable(weights1_init, dtype=tf.float32, name="weights1")
weights2 = tf.Variable(weights2_init, dtype=tf.float32, name="weights2")
weights3 = tf.Variable(weights3_init, dtype=tf.float32, name="weights3")
weights4 = tf.Variable(weights4_init, dtype=tf.float32, name="weights4")
biases1 = tf.Variable(tf.zeros(n_hidden1), name="biases1")
biases2 = tf.Variable(tf.zeros(n_hidden2), name="biases2")
biases3 = tf.Variable(tf.zeros(n_hidden3), name="biases3")
biases4 = tf.Variable(tf.zeros(n_outputs), name="biases4")
hidden1 = activation(tf.matmul(X, weights1) + biases1)
hidden2 = activation(tf.matmul(hidden1, weights2) + biases2)
hidden3 = activation(tf.matmul(hidden2, weights3) + biases3)
outputs = tf.matmul(hidden3, weights4) + biases4
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X))
optimizer = tf.train.AdamOptimizer(learning_rate)
# 第一阶段
with tf.name_scope("phase1"):
phase1_outputs = tf.matmul(hidden1, weights4) + biases4 # bypass hidden2 and hidden3
phase1_reconstruction_loss = tf.reduce_mean(tf.square(phase1_outputs - X))
phase1_reg_loss = regularizer(weights1) + regularizer(weights4)
phase1_loss = phase1_reconstruction_loss + phase1_reg_loss
phase1_training_op = optimizer.minimize(phase1_loss)
# 第二阶段
with tf.name_scope("phase2"):
phase2_reconstruction_loss = tf.reduce_mean(tf.square(hidden3 - hidden1))
phase2_reg_loss = regularizer(weights2) + regularizer(weights3)
phase2_loss = phase2_reconstruction_loss + phase2_reg_loss
train_vars = [weights2, biases2, weights3, biases3]
phase2_training_op = optimizer.minimize(phase2_loss, var_list=train_vars) # freeze hidden1
init = tf.global_variables_initializer()
saver = tf.train.Saver()
training_ops = [phase1_training_op, phase2_training_op]
reconstruction_losses = [phase1_reconstruction_loss, phase2_reconstruction_loss]
n_epochs = [4, 4]
batch_sizes = [150, 150]
with tf.Session() as sess:
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
init.run()
for phase in range(2):
print("Training phase #{}".format(phase + 1))
for epoch in range(n_epochs[phase]):
n_batches = mnist.train.num_examples // batch_sizes[phase]
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
X_batch, y_batch = mnist.train.next_batch(batch_sizes[phase])
sess.run(training_ops[phase], feed_dict={
X: X_batch})
loss_train = reconstruction_losses[phase].eval(feed_dict={
X: X_batch})
print("\r{}".format(epoch), "Train MSE:", loss_train)
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_one_at_a_time.ckpt")
loss_test = reconstruction_loss.eval(feed_dict={
X: mnist.test.images})
print("Test MSE:", loss_test)
运行结果:
馈送冻结层输出缓存
由于隐藏层 1 在阶段 2 期间被冻结,所以对于任何给定的训练实例其输出将总是相同的。为了避免在每个时期重新计算隐藏层1的输出,可以在阶段 1 结束时为整个训练集计算它,然后直接在阶段 2 中输入隐藏层 1 的缓存输出。这可以得到一个不错的性能上的提升。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_8.py
@time: 2019/7/2 9:32
@desc: 馈送冻结层输出缓存
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import numpy.random as rnd
import sys
n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 150 # codings
n_hidden3 = n_hidden1
n_outputs = n_inputs
learning_rate = 0.01
l2_reg = 0.0001
activation = tf.nn.elu
regularizer = tf.contrib.layers.l2_regularizer(l2_reg)
initializer = tf.contrib.layers.variance_scaling_initializer()
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
weights1_init = initializer([n_inputs, n_hidden1])
weights2_init = initializer([n_hidden1, n_hidden2])
weights3_init = initializer([n_hidden2, n_hidden3])
weights4_init = initializer([n_hidden3, n_outputs])
weights1 = tf.Variable(weights1_init, dtype=tf.float32, name="weights1")
weights2 = tf.Variable(weights2_init, dtype=tf.float32, name="weights2")
weights3 = tf.Variable(weights3_init, dtype=tf.float32, name="weights3")
weights4 = tf.Variable(weights4_init, dtype=tf.float32, name="weights4")
biases1 = tf.Variable(tf.zeros(n_hidden1), name="biases1")
biases2 = tf.Variable(tf.zeros(n_hidden2), name="biases2")
biases3 = tf.Variable(tf.zeros(n_hidden3), name="biases3")
biases4 = tf.Variable(tf.zeros(n_outputs), name="biases4")
hidden1 = activation(tf.matmul(X, weights1) + biases1)
hidden2 = activation(tf.matmul(hidden1, weights2) + biases2)
hidden3 = activation(tf.matmul(hidden2, weights3) + biases3)
outputs = tf.matmul(hidden3, weights4) + biases4
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X))
optimizer = tf.train.AdamOptimizer(learning_rate)
# 第一阶段
with tf.name_scope("phase1"):
phase1_outputs = tf.matmul(hidden1, weights4) + biases4 # bypass hidden2 and hidden3
phase1_reconstruction_loss = tf.reduce_mean(tf.square(phase1_outputs - X))
phase1_reg_loss = regularizer(weights1) + regularizer(weights4)
phase1_loss = phase1_reconstruction_loss + phase1_reg_loss
phase1_training_op = optimizer.minimize(phase1_loss)
# 第二阶段
with tf.name_scope("phase2"):
phase2_reconstruction_loss = tf.reduce_mean(tf.square(hidden3 - hidden1))
phase2_reg_loss = regularizer(weights2) + regularizer(weights3)
phase2_loss = phase2_reconstruction_loss + phase2_reg_loss
train_vars = [weights2, biases2, weights3, biases3]
phase2_training_op = optimizer.minimize(phase2_loss, var_list=train_vars) # freeze hidden1
init = tf.global_variables_initializer()
saver = tf.train.Saver()
training_ops = [phase1_training_op, phase2_training_op]
reconstruction_losses = [phase1_reconstruction_loss, phase2_reconstruction_loss]
n_epochs = [4, 4]
batch_sizes = [150, 150]
with tf.Session() as sess:
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
init.run()
for phase in range(2):
print("Training phase #{}".format(phase + 1))
if phase == 1:
hidden1_cache = hidden1.eval(feed_dict={
X: mnist.train.images})
for epoch in range(n_epochs[phase]):
n_batches = mnist.train.num_examples // batch_sizes[phase]
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
if phase == 1:
indices = rnd.permutation(mnist.train.num_examples)
hidden1_batch = hidden1_cache[indices[:batch_sizes[phase]]]
feed_dict = {
hidden1: hidden1_batch}
sess.run(training_ops[phase], feed_dict=feed_dict)
else:
X_batch, y_batch = mnist.train.next_batch(batch_sizes[phase])
feed_dict = {
X: X_batch}
sess.run(training_ops[phase], feed_dict=feed_dict)
loss_train = reconstruction_losses[phase].eval(feed_dict=feed_dict)
print("\r{}".format(epoch), "Train MSE:", loss_train)
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_cache_frozen.ckpt")
loss_test = reconstruction_loss.eval(feed_dict={
X: mnist.test.images})
print("Test MSE:", loss_test)
运行结果:
重建可视化
确保自编码器得到适当训练的一种方法是比较输入和输出。 它们必须非常相似,差异应该是不重要的细节。 我们来绘制两个随机数字及其重建:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_9.py
@time: 2019/7/2 9:38
@desc: 重建可视化
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 150 # codings
n_hidden3 = n_hidden1
n_outputs = n_inputs
learning_rate = 0.01
l2_reg = 0.0001
activation = tf.nn.elu
regularizer = tf.contrib.layers.l2_regularizer(l2_reg)
initializer = tf.contrib.layers.variance_scaling_initializer()
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
weights1_init = initializer([n_inputs, n_hidden1])
weights2_init = initializer([n_hidden1, n_hidden2])
weights3_init = initializer([n_hidden2, n_hidden3])
weights4_init = initializer([n_hidden3, n_outputs])
weights1 = tf.Variable(weights1_init, dtype=tf.float32, name="weights1")
weights2 = tf.Variable(weights2_init, dtype=tf.float32, name="weights2")
weights3 = tf.Variable(weights3_init, dtype=tf.float32, name="weights3")
weights4 = tf.Variable(weights4_init, dtype=tf.float32, name="weights4")
biases1 = tf.Variable(tf.zeros(n_hidden1), name="biases1")
biases2 = tf.Variable(tf.zeros(n_hidden2), name="biases2")
biases3 = tf.Variable(tf.zeros(n_hidden3), name="biases3")
biases4 = tf.Variable(tf.zeros(n_outputs), name="biases4")
hidden1 = activation(tf.matmul(X, weights1) + biases1)
hidden2 = activation(tf.matmul(hidden1, weights2) + biases2)
hidden3 = activation(tf.matmul(hidden2, weights3) + biases3)
outputs = tf.matmul(hidden3, weights4) + biases4
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
n_test_digits = 2
X_test = mnist.test.images[:n_test_digits]
saver = tf.train.Saver()
with tf.Session() as sess:
saver.restore(sess, "D:/Python3Space/BookStudy/book4/model/my_model_one_at_a_time.ckpt") # not shown in the book
outputs_val = outputs.eval(feed_dict={
X: X_test})
def plot_image(image, shape=[28, 28]):
plt.imshow(image.reshape(shape), cmap="Greys", interpolation="nearest")
plt.axis("off")
for digit_index in range(n_test_digits):
plt.subplot(n_test_digits, 2, digit_index * 2 + 1)
plot_image(X_test[digit_index])
plt.subplot(n_test_digits, 2, digit_index * 2 + 2)
plot_image(outputs_val[digit_index])
plt.show()
运行结果:
特征可视化
一旦你的自编码器学习了一些功能,你可能想看看它们。 有各种各样的技术。 可以说最简单的技术是在每个隐藏层中考虑每个神经元,并找到最能激活它的训练实例。
对于第一个隐藏层中的每个神经元,您可以创建一个图像,其中像素的强度对应于给定神经元的连接权重。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_10.py
@time: 2019/7/2 9:49
@desc: 特征可视化
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 150 # codings
n_hidden3 = n_hidden1
n_outputs = n_inputs
learning_rate = 0.01
l2_reg = 0.0001
activation = tf.nn.elu
regularizer = tf.contrib.layers.l2_regularizer(l2_reg)
initializer = tf.contrib.layers.variance_scaling_initializer()
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
weights1_init = initializer([n_inputs, n_hidden1])
weights2_init = initializer([n_hidden1, n_hidden2])
weights3_init = initializer([n_hidden2, n_hidden3])
weights4_init = initializer([n_hidden3, n_outputs])
weights1 = tf.Variable(weights1_init, dtype=tf.float32, name="weights1")
weights2 = tf.Variable(weights2_init, dtype=tf.float32, name="weights2")
weights3 = tf.Variable(weights3_init, dtype=tf.float32, name="weights3")
weights4 = tf.Variable(weights4_init, dtype=tf.float32, name="weights4")
biases1 = tf.Variable(tf.zeros(n_hidden1), name="biases1")
biases2 = tf.Variable(tf.zeros(n_hidden2), name="biases2")
biases3 = tf.Variable(tf.zeros(n_hidden3), name="biases3")
biases4 = tf.Variable(tf.zeros(n_outputs), name="biases4")
hidden1 = activation(tf.matmul(X, weights1) + biases1)
hidden2 = activation(tf.matmul(hidden1, weights2) + biases2)
hidden3 = activation(tf.matmul(hidden2, weights3) + biases3)
outputs = tf.matmul(hidden3, weights4) + biases4
saver = tf.train.Saver()
# 在复制一遍可视化代码
def plot_image(image, shape=[28, 28]):
plt.imshow(image.reshape(shape), cmap="Greys", interpolation="nearest")
plt.axis("off")
with tf.Session() as sess:
saver.restore(sess, "D:/Python3Space/BookStudy/book4/model/my_model_one_at_a_time.ckpt") # not shown in the book
weights1_val = weights1.eval()
for i in range(5):
plt.subplot(1, 5, i + 1)
plot_image(weights1_val.T[i])
plt.show()
运行结果:
第一层隐藏层为300个神经元,权重为[28*28, 300]。
图中为可视化前五个神经元。
使用堆叠的自动编码器进行无监督的预训练
参考链接:Numpy.random中shuffle与permutation的区别
函数shuffle与permutation都是对原来的数组进行重新洗牌(即随机打乱原来的元素顺序);区别在于shuffle直接在原来的数组上进行操作,改变原来数组的顺序,无返回值。而permutation不直接在原来的数组上进行操作,而是返回一个新的打乱顺序的数组,并不改变原来的数组。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_4.py
@time: 2019/6/23 16:50
@desc: 使用堆叠的自动编码器进行无监督的预训练
"""
import tensorflow as tf
from tensorflow.contrib.layers import fully_connected
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import sys
import numpy.random as rnd
n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 150
n_outputs = 10
learning_rate = 0.01
l2_reg = 0.0005
activation = tf.nn.elu
regularizer = tf.contrib.layers.l2_regularizer(l2_reg)
initializer = tf.contrib.layers.variance_scaling_initializer()
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
y = tf.placeholder(tf.int32, shape=[None])
weights1_init = initializer([n_inputs, n_hidden1])
weights2_init = initializer([n_hidden1, n_hidden2])
weights3_init = initializer([n_hidden2, n_outputs])
weights1 = tf.Variable(weights1_init, dtype=tf.float32, name="weights1")
weights2 = tf.Variable(weights2_init, dtype=tf.float32, name="weights2")
weights3 = tf.Variable(weights3_init, dtype=tf.float32, name="weights3")
biases1 = tf.Variable(tf.zeros(n_hidden1), name="biases1")
biases2 = tf.Variable(tf.zeros(n_hidden2), name="biases2")
biases3 = tf.Variable(tf.zeros(n_outputs), name="biases3")
hidden1 = activation(tf.matmul(X, weights1) + biases1)
hidden2 = activation(tf.matmul(hidden1, weights2) + biases2)
logits = tf.matmul(hidden2, weights3) + biases3
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
reg_loss = regularizer(weights1) + regularizer(weights2) + regularizer(weights3)
loss = cross_entropy + reg_loss
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))
init = tf.global_variables_initializer()
pretrain_saver = tf.train.Saver([weights1, weights2, biases1, biases2])
saver = tf.train.Saver()
n_epochs = 4
batch_size = 150
n_labeled_instances = 20000
with tf.Session() as sess:
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
init.run()
for epoch in range(n_epochs):
n_batches = n_labeled_instances // batch_size
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
indices = rnd.permutation(n_labeled_instances)[:batch_size]
X_batch, y_batch = mnist.train.images[indices], mnist.train.labels[indices]
sess.run(training_op, feed_dict={
X: X_batch, y: y_batch})
accuracy_val = accuracy.eval(feed_dict={
X: X_batch, y: y_batch})
print("\r{}".format(epoch), "Train accuracy:", accuracy_val, end=" ")
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_supervised.ckpt")
accuracy_val = accuracy.eval(feed_dict={
X: mnist.test.images, y: mnist.test.labels})
print("Test accuracy:", accuracy_val)
运行结果
去噪自动编码器
另一种强制自动编码器学习有用特征的方法是在输入中增加噪音,训练它以恢复原始的无噪音输入。这种方法阻止了自动编码器简单的复制其输入到输出,最终必须找到数据中的模式。
自动编码器可以用于特征提取。
这里要用tf.shape(X)
来获取图片的size,它创建了一个在运行时返回该点完全定义的X向量的操作;而不能用X.get_shape()
,因为这将只返回部分定义的X([None], n_inputs)
向量。
使用高斯噪音
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_11.py
@time: 2019/7/2 10:02
@desc: 去噪自动编码器:使用高斯噪音
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import sys
n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 150 # codings
n_hidden3 = n_hidden1
n_outputs = n_inputs
learning_rate = 0.01
noise_level = 1.0
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
X_noisy = X + noise_level * tf.random_normal(tf.shape(X))
hidden1 = tf.layers.dense(X_noisy, n_hidden1, activation=tf.nn.relu,
name="hidden1")
hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, # not shown in the book
name="hidden2") # not shown
hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, # not shown
name="hidden3") # not shown
outputs = tf.layers.dense(hidden3, n_outputs, name="outputs") # not shown
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X)) # MSE
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(reconstruction_loss)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 10
batch_size = 150
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
n_batches = mnist.train.num_examples // batch_size
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
X_batch, y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op, feed_dict={
X: X_batch})
loss_train = reconstruction_loss.eval(feed_dict={
X: X_batch})
print("\r{}".format(epoch), "Train MSE:", loss_train)
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_stacked_denoising_gaussian.ckpt")
运行结果:
使用dropout
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_12.py
@time: 2019/7/2 10:11
@desc: 去噪自动编码器:使用dropout
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import sys
n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 150 # codings
n_hidden3 = n_hidden1
n_outputs = n_inputs
learning_rate = 0.01
dropout_rate = 0.3
training = tf.placeholder_with_default(False, shape=(), name='training')
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
X_drop = tf.layers.dropout(X, dropout_rate, training=training)
hidden1 = tf.layers.dense(X_drop, n_hidden1, activation=tf.nn.relu,
name="hidden1")
hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, # not shown in the book
name="hidden2") # not shown
hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, # not shown
name="hidden3") # not shown
outputs = tf.layers.dense(hidden3, n_outputs, name="outputs") # not shown
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X)) # MSE
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(reconstruction_loss)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 10
batch_size = 150
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
n_batches = mnist.train.num_examples // batch_size
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
X_batch, y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op, feed_dict={
X: X_batch, training: True})
loss_train = reconstruction_loss.eval(feed_dict={
X: X_batch})
print("\r{}".format(epoch), "Train MSE:", loss_train)
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_stacked_denoising_dropout.ckpt")
运行结果:
在测试期间没有必要设置is_training
为False
,因为调用placeholder_with_default()
函数时我们设置其为默认值。
稀疏自动编码器
通常良好特征提取的另一种约束是稀疏性:通过向损失函数添加适当的项,自编码器被推动以减少编码层中活动神经元的数量。
一种方法可以简单地将平方误差添加到损失函数中,但实际上更好的方法是使用 Kullback-Leibler 散度(相对熵),其具有比均方误差更强的梯度。它表示2个函数或概率分布的差异性:差异越大则相对熵越大,差异越小则相对熵越小,特别地,若2者相同则熵为0。注意,KL散度的非对称性。
一旦我们已经计算了编码层中每个神经元的稀疏损失,我们就总结这些损失,并将结果添加到损失函数中。 为了控制稀疏损失和重构损失的相对重要性,我们可以用稀疏权重超参数乘以稀疏损失。 如果这个权重太高,模型会紧贴目标稀疏度,但它可能无法正确重建输入,导致模型无用。 相反,如果它太低,模型将大多忽略稀疏目标,它不会学习任何有用的功能。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_13.py
@time: 2019/7/2 10:32
@desc: 稀疏自动编码器
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import sys
n_inputs = 28 * 28
n_hidden1 = 1000 # sparse codings
n_outputs = n_inputs
def kl_divergence(p, q):
# Kullback Leibler divergence
return p * tf.log(p / q) + (1 - p) * tf.log((1 - p) / (1 - q))
learning_rate = 0.01
sparsity_target = 0.1
sparsity_weight = 0.2
X = tf.placeholder(tf.float32, shape=[None, n_inputs]) # not shown in the book
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.sigmoid) # not shown
outputs = tf.layers.dense(hidden1, n_outputs) # not shown
hidden1_mean = tf.reduce_mean(hidden1, axis=0) # batch mean
sparsity_loss = tf.reduce_sum(kl_divergence(sparsity_target, hidden1_mean))
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X)) # MSE
loss = reconstruction_loss + sparsity_weight * sparsity_loss
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
# 训练过程
n_epochs = 100
batch_size = 1000
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
n_batches = mnist.train.num_examples // batch_size
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
X_batch, y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op, feed_dict={
X: X_batch})
reconstruction_loss_val, sparsity_loss_val, loss_val = sess.run([reconstruction_loss, sparsity_loss, loss], feed_dict={
X: X_batch})
print("\r{}".format(epoch), "Train MSE:", reconstruction_loss_val, "\tSparsity loss:", sparsity_loss_val, "\tTotal loss:", loss_val)
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_sparse.ckpt")
运行结果:
一个重要的细节是,编码器激活度的值是在0-1之间(但是不等于0或1),否则KL散度将返回NaN。一个简单的解决方案是为编码层使用逻辑激活函数:hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.sigmoid)
一个简单的技巧可以加速收敛:不是使用 MSE,我们可以选择一个具有较大梯度的重建损失。 交叉熵通常是一个不错的选择。 要使用它,我们必须对输入进行规范化处理,使它们的取值范围为 0 到 1,并在输出层中使用逻辑激活函数,以便输出也取值为 0 到 1。TensorFlow 的sigmoid_cross_entropy_with_logits()
函数负责有效地将logistic(sigmoid)激活函数应用于输出并计算交叉熵。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_14.py
@time: 2019/7/2 10:53
@desc: 稀疏自动编码器:加速收敛方法
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import sys
n_inputs = 28 * 28
n_hidden1 = 1000 # sparse codings
n_outputs = n_inputs
def kl_divergence(p, q):
# Kullback Leibler divergence
return p * tf.log(p / q) + (1 - p) * tf.log((1 - p) / (1 - q))
learning_rate = 0.01
sparsity_target = 0.1
sparsity_weight = 0.2
X = tf.placeholder(tf.float32, shape=[None, n_inputs]) # not shown in the book
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.sigmoid) # not shown
logits = tf.layers.dense(hidden1, n_outputs) # not shown
outputs = tf.nn.sigmoid(logits)
hidden1_mean = tf.reduce_mean(hidden1, axis=0) # batch mean
sparsity_loss = tf.reduce_sum(kl_divergence(sparsity_target, hidden1_mean))
xentropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=X, logits=logits)
reconstruction_loss = tf.reduce_mean(xentropy)
loss = reconstruction_loss + sparsity_weight * sparsity_loss
optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
# 训练过程
n_epochs = 100
batch_size = 1000
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
n_batches = mnist.train.num_examples // batch_size
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
X_batch, y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op, feed_dict={
X: X_batch})
reconstruction_loss_val, sparsity_loss_val, loss_val = sess.run([reconstruction_loss, sparsity_loss, loss], feed_dict={
X: X_batch})
print("\r{}".format(epoch), "Train MSE:", reconstruction_loss_val, "\tSparsity loss:", sparsity_loss_val, "\tTotal loss:", loss_val)
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_sparse_speedup.ckpt")
运行结果:
变分自动编码器
它和前面所讨论的所有编码器都不同,特别之处在于:
它们是概率自动编码器,这就意味着即使经过训练,其输出也部分程度上决定于运气(不同于仅在训练期间使用随机性的去噪自动编码器)。
更重要的是,它们是生成自动编码器,意味着它们可以生成看起来像是从训练样本采样的新实例。
参考文献: 变分自编码器(一):原来是这么一回事
VAE的名字中“变分”,是因为它的推导过程用到了KL散度及其性质。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_15.py
@time: 2019/7/3 9:42
@desc: 变分自动编码器
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import sys
from functools import partial
n_inputs = 28 * 28
n_hidden1 = 500
n_hidden2 = 500
n_hidden3 = 20 # codings
n_hidden4 = n_hidden2
n_hidden5 = n_hidden1
n_outputs = n_inputs
learning_rate = 0.001
initializer = tf.contrib.layers.variance_scaling_initializer()
my_dense_layer = partial(
tf.layers.dense,
activation=tf.nn.elu,
kernel_initializer=initializer)
X = tf.placeholder(tf.float32, [None, n_inputs])
hidden1 = my_dense_layer(X, n_hidden1)
hidden2 = my_dense_layer(hidden1, n_hidden2)
hidden3_mean = my_dense_layer(hidden2, n_hidden3, activation=None)
hidden3_sigma = my_dense_layer(hidden2, n_hidden3, activation=None)
hidden3_gamma = my_dense_layer(hidden2, n_hidden3, activation=None)
noise = tf.random_normal(tf.shape(hidden3_sigma), dtype=tf.float32)
hidden3 = hidden3_mean + hidden3_sigma * noise
hidden4 = my_dense_layer(hidden3, n_hidden4)
hidden5 = my_dense_layer(hidden4, n_hidden5)
logits = my_dense_layer(hidden5, n_outputs, activation=None)
outputs = tf.sigmoid(logits)
xentropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=X, logits=logits)
reconstruction_loss = tf.reduce_sum(xentropy)
eps = 1e-10 # smoothing term to avoid computing log(0) which is NaN
latent_loss = 0.5 * tf.reduce_sum(
tf.square(hidden3_sigma) + tf.square(hidden3_mean)
- 1 - tf.log(eps + tf.square(hidden3_sigma)))
# 一个常见的变体
# latent_loss = 0.5 * tf.reduce_sum(
# tf.exp(hidden3_gamma) + tf.square(hidden3_mean) - 1 - hidden3_gamma)
loss = reconstruction_loss + latent_loss
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 50
batch_size = 150
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
n_batches = mnist.train.num_examples // batch_size
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="")
sys.stdout.flush()
X_batch, y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op, feed_dict={
X: X_batch})
loss_val, reconstruction_loss_val, latent_loss_val = sess.run([loss, reconstruction_loss, latent_loss], feed_dict={
X: X_batch})
print("\r{}".format(epoch), "Train total loss:", loss_val, "\tReconstruction loss:", reconstruction_loss_val, "\tLatent loss:", latent_loss_val)
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_variational.ckpt")
运行结果
生成数字
用上述变分自动编码器生成一些看起来像手写数字的图片。我们需要做的是训练模型,然后从高斯分布中随机采样编码并对其进行解码。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# coding=utf-8
"""
@author: Li Tian
@contact: [email protected]
@software: pycharm
@file: autoencoder_16.py
@time: 2019/7/3 10:12
@desc: 生成数字
"""
import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import sys
from functools import partial
import matplotlib.pyplot as plt
n_inputs = 28 * 28
n_hidden1 = 500
n_hidden2 = 500
n_hidden3 = 20 # codings
n_hidden4 = n_hidden2
n_hidden5 = n_hidden1
n_outputs = n_inputs
learning_rate = 0.001
initializer = tf.contrib.layers.variance_scaling_initializer()
my_dense_layer = partial(
tf.layers.dense,
activation=tf.nn.elu,
kernel_initializer=initializer)
X = tf.placeholder(tf.float32, [None, n_inputs])
hidden1 = my_dense_layer(X, n_hidden1)
hidden2 = my_dense_layer(hidden1, n_hidden2)
hidden3_mean = my_dense_layer(hidden2, n_hidden3, activation=None)
hidden3_sigma = my_dense_layer(hidden2, n_hidden3, activation=None)
hidden3_gamma = my_dense_layer(hidden2, n_hidden3, activation=None)
noise = tf.random_normal(tf.shape(hidden3_sigma), dtype=tf.float32)
hidden3 = hidden3_mean + hidden3_sigma * noise
hidden4 = my_dense_layer(hidden3, n_hidden4)
hidden5 = my_dense_layer(hidden4, n_hidden5)
logits = my_dense_layer(hidden5, n_outputs, activation=None)
outputs = tf.sigmoid(logits)
xentropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=X, logits=logits)
reconstruction_loss = tf.reduce_sum(xentropy)
eps = 1e-10 # smoothing term to avoid computing log(0) which is NaN
latent_loss = 0.5 * tf.reduce_sum(
tf.square(hidden3_sigma) + tf.square(hidden3_mean)
- 1 - tf.log(eps + tf.square(hidden3_sigma)))
# 一个常见的变体
# latent_loss = 0.5 * tf.reduce_sum(
# tf.exp(hidden3_gamma) + tf.square(hidden3_mean) - 1 - hidden3_gamma)
loss = reconstruction_loss + latent_loss
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_digits = 60
n_epochs = 50
batch_size = 150
mnist = input_data.read_data_sets('D:/Python3Space/BookStudy/book2/MNIST_data/')
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
n_batches = mnist.train.num_examples // batch_size
for iteration in range(n_batches):
print("\r{}%".format(100 * iteration // n_batches), end="") # not shown in the book
sys.stdout.flush() # not shown
X_batch, y_batch = mnist.train.next_batch(batch_size)
sess.run(training_op, feed_dict={
X: X_batch})
loss_val, reconstruction_loss_val, latent_loss_val = sess.run([loss, reconstruction_loss, latent_loss],
feed_dict={
X: X_batch}) # not shown
print("\r{}".format(epoch), "Train total loss:", loss_val, "\tReconstruction loss:", reconstruction_loss_val,
"\tLatent loss:", latent_loss_val) # not shown
saver.save(sess, "D:/Python3Space/BookStudy/book4/model/my_model_variational2.ckpt") # not shown
codings_rnd = np.random.normal(size=[n_digits, n_hidden3])
outputs_val = outputs.eval(feed_dict={
hidden3: codings_rnd})
def plot_image(image, shape=[28, 28]):
plt.imshow(image.reshape(shape), cmap="Greys", interpolation="nearest")
plt.axis("off")
def plot_multiple_images(images, n_rows, n_cols, pad=2):
images = images - images.min() # make the minimum == 0, so the padding looks white
w,h = images.shape[1:]
image = np.zeros(((w+pad)*n_rows+pad, (h+pad)*n_cols+pad))
for y in range(n_rows):
for x in range(n_cols):
image[(y*(h+pad)+pad):(y*(h+pad)+pad+h),(x*(w+pad)+pad):(x*(w+pad)+pad+w)] = images[y*n_cols+x]
plt.imshow(image, cmap="Greys", interpolation="nearest")
plt.axis("off")
plt.figure(figsize=(8,50)) # not shown in the book
for iteration in range(n_digits):
plt.subplot(n_digits, 10, iteration + 1)
plot_image(outputs_val[iteration])
plt.show()
n_rows = 6
n_cols = 10
plot_multiple_images(outputs_val.reshape(-1, 28, 28), n_rows, n_cols)
plt.show()
运行结果:
其他自动编码器
部分课后题的摘抄
自动编码器的主要任务是什么?
加入想训练一个分类器,而且有大量未训练的数据,但是只有几千个已经标记的实例。自动编码器可以如何帮助你?你会如何实现?
首先可以在完整的数据集(已标记和未标记)上训练一个深度自动编码器,然后重用其下半部分作为分类器(即重用上半部分作为编码层)并使用已标记的数据训练分类器。如果已标记的数据集比较小,你可能希望在训练分类器时冻结复用层。
如果自动编码器可以完美的重现输入,它就是一个好的自动编码器吗?如何评估自动编码器的性能?
完美的重建并不能保证自动编码器学习到有用的东西。然而,如果产生非常差的重建,它几乎必然是一个糟糕的自动编码器。
为了评估编码器的性能,一种方法是测量重建损失(例如,计算MSE,用输入的均方值减去输入)。同样,重建损失高意味着这是一个不好的编码器,但是重建损失低并不能保证这是一个好的自动编码器。你应该根据它的用途来评估自动编码器。例如,如果它用于无监督分类器的预训练,同样应该评估分类器的性能。
什么是不完整和完整自动编码器?不啊完整自动编码器的主要风险是什么?完整的意义是什么?
不完整的自动编码器是**编码层比输入和输出层小**的自动编码器。如果比其大,那就是一个完整的自动编码器。一个过度不完整的自动编码器的主要风险是:不能重建其输入。完整的自动编码器的主要风险是:它可能只是将输入复制到输出,不学习任何有用的特征。
如何在栈式自动编码器上绑定权重?为什么要这样做?
要将自动编码器的权重与其相应的解码层相关联,你可以简单的使得解码权重等于编码权重的转置。这将使得模型参数数量减少一半,通常使得训练在数据较少时快速收敛,减少过度拟合的危险。
栈式自动编码器低层学习可视化特征的常用技术是什么?高层又是什么?
为了可视化栈式自动编码器低层学习到的特征,一个常用的技术是通过将每个权重向量重建为输入图像的大小来绘制每个神经元的权重(例如,对MNIST,将权重向量形状[784]重建为[28, 28])。为了可视化高层学习到的特征,一种技术是显示最能激活每个神经元的训练实例。
什么是生成模型?你能列举一种生成自动编码器吗?
生成模型是一种可以随机生成类似于训练实例的输出的模型。
例如,一旦在MNIST数据集上训练成功,生成模型就可以用于随机生成逼真的数字图像。输出分布大致和训练数据类似。例如,因为MNIST包含了每个数字的多个图像,生成模型可以输出与每个数字大致数量相同的图像。有些生成模型可以进行参数化,例如,生成某种类型的输出。生成自动编码器的一个例子是变分自动编码器。
我的CSDN:https://blog.csdn.net/qq_21579045
我的博客园:https://www.cnblogs.com/lyjun/
我的Github:https://github.com/TinyHandsome
纸上得来终觉浅,绝知此事要躬行~
欢迎大家过来OB~
by 李英俊小朋友