作者:韩信子@ShowMeAI
深度学习实战系列:https://www.showmeai.tech/tutorials/42
TensorFlow实战系列: https://www.showmeai.tech/tutorials/43
本文地址:https://www.showmeai.tech/article-detail/290
申明:版权所有,转载请分割平台与作者并注明出处
珍藏ShowMeAI查看更多精彩内容
深度学习是机器学习的一类算法,它利用各种构造的神经网络解决问题(深度学习中的『深度』指的是咱们会通过深层次的神经网络构建弱小的学习器),模拟人类取得某些类型常识的形式,与传统机器学习模型相比,神经网络有更灵便的结构设计,更强的学习能力,能撑持更大量级的数据学习,因而宽泛援用于各种业务中。
神经网络是简化人脑的学习思考过程构建的构造,它是一个连贯单元(神经元)的连贯重叠构造,咱们设计这些单元,心愿它们能在肯定水平上模拟大脑中的突触,将信号传递给其余神经元,就像相互连接的脑细胞一样,能够在更多的工夫里学习和做出决定。如下是简略的神经网络拆解图。
深度学习与神经网络有很多不同的构造和利用,想要零碎学习这部分常识的同学,能够查看ShowMeAI制作的下列教程:
- 深度学习教程 | 吴恩达专项课程 · 全套笔记解读
- 自然语言解决教程 | 斯坦福CS224n课程 · 课程带学与全套笔记解读
- 深度学习与计算机视觉教程:斯坦福CS231n · 全套笔记解读
ShowMeAI 将在本文中,全面图解展现应用 Python 构建神经网络的过程,笼罩TensorFlow建模、可视化网络、模型预测后果归因与解释。
文中解说到的板块和对应的实现 Python 代码,能够很容易地迁徙利用于其余相似状况(复制、粘贴、运行),咱们对代码做了详尽的正文解说。
全文的总体内容构造包含:
- 环境设置与TensorFlow工具库简介
- 神经网络合成、输出、输入、暗藏层、激活函数
- 应用深度神经网络进行深度学习
- 进行模型设计(基于TensorFlow)
- 可视化神经网络
- 模型训练和测试
- 模型可解释性
环境设置
目前支流的神经网络工具库有2个:TensorFlow https://www.tensorflow.org/(由 Google 开发)和 PyTorch https://pytorch.org/ (由 Facebook 开发) 。 他们有很多相似之处,性能也都很全面,但总体来说前者更适宜生产,而后者更适宜构建疾速原型。
这两个库都能够利用 GPU 的弱小矩阵运算性能去减速神经网络的训练和预估,这对于解决大型数据集(如文本语料库或图像库)十分有用,而其对应的开发社区也有着丰盛的资源,不论你解决何种问题,总能够找到相干的参考资料。
本篇内容应用到的是 TensorFlow 工具库。
对于本篇应用到的工具,ShowMeAI制作了快捷即查即用的工具速查表手册,大家能够在下述地位取得:
- TensorFlow速查手册
- Matplotlib速查手册
咱们先在终端通过 pip install 命令装置 TensorFlow。
pip install tensorflow
当初咱们能够在 Notebook 上导入 TensorFlow Keras 并开始编码:
# 导入所需的工具库# tensorflow建模from tensorflow.keras import models, layers, utils, backend as K# 可视化import matplotlib.pyplot as plt# 特色重要度与模型归因剖析import shap
神经网络拆解
神经网络的构造拆解的具体内容,举荐大家查看ShowMeAI的教程 深度学习教程 | 吴恩达专项课程 · 全套笔记解读下述文章:
- 深度学习教程 | 神经网络根底
- 深度学习教程 | 浅层神经网络
- 深度学习教程 | 深层神经网络
人工神经网络由若干层组成(每一层有独立的输出和输入维度)。这些层能够分组为:
- 输出层 : 负责将输出向量传递给神经网络。如果咱们有一个蕴含 3 个特色的矩阵(形态 N x 3),则该层将 3 个数字作为输出,并将雷同的 3 个数字传递给下一层。
- 暗藏层 : 代表两头节点,它们对数字进行屡次变换以进步最终后果的准确性,输入由神经元的数量定义。
- 输入层 : 返回神经网络最终输入的 如果咱们进行简略的二元分类或回归,输入层应该只有 1 个神经元(因而它只返回 1 个数字)。在具备 5 个不同类别的多类别分类的状况下,输入层应有 5 个神经元。
最简略的神经网络模式是感知器,一个只有一层的模型,与线性回归/逻辑回归模型十分类似。
举个例子:假如咱们有一个蕴含 N 行、3 个特色和 1 个指标变量(二分类,取值0或1)的数据集,如下图所示:
实际上,数据在输出神经网络之前应该进行幅度缩放,咱们这里举例的输出数据间接用了0-1之间的值。相似其余机器学习模型,咱们应用输出X去预测输入y:
而当咱们提到『训练模型』时,咱们指的是寻找最佳参数,使得模型预测的后果能最精确地预估目标值。
这里的最佳参数,在不同的情景下,有不同的解释:
- 在线性回归中,是找到最佳权重w
- 在基于树的模型(比方随机森林)中,它是找到最佳决裂点
- 如下的感知器中,咱们心愿找到最佳的W(w1,w2,w3)
咱们有一些权重初始化办法,在这里咱们采纳最简略的随机初始化,而后随着学习的进行调整优化参数。如下图,咱们将权重 w 全副初始化为 1:
接下来咱们要进行一个简略的计算来对后果进行预估,上面的操作相似于单个神经网络的计算,f(WX+b),其中f函数叫做激活函数。
激活函数是非线性的映射函数,使得神经网络具备弱小的非线性拟合学习能力,如下是一些激活函数示意图(当然,理论咱们可能会更多地应用ReLU等激活函数)。
激活函数具体解说能够参考ShowMeAI的文章 深度学习教程 | 浅层神经网络。
退出咱们采纳下面的阶跃激活函数,那简略的计算过程如下:
咱们失去了感知器的输入,这是一个单层神经网络,它承受一些输出并返回 1 个输入。当初模型的训练将持续通过将输入与指标进行比拟,计算误差并优化权重,一遍又一遍地反复整个过程。
总结一下,这就是最简略的神经元,简化的构造示意如下:
深度神经网络
能够说所有深度学习模型都是神经网络,但并非所有神经网络都是深度学习模型。一般来说,『深度』学习实用于算法至多有 2 个暗藏层(因而总共 4 层,包含输出和输入)。
对于深度神经网络的具体常识,大家能够浏览学习ShowMeAI的文章 深度学习教程 | 深层神经网络。
设想一下在中间层增加3个和方才一样的神经元:因为每个节点(加权和和激活函数)都返回一个值,咱们将失去具备 3 个输入的 暗藏层。
接下来咱们应用这 3 个输入作为 第2个暗藏层 的输出,第2个暗藏层也同样计算失去 3 个后果值。最初,咱们将增加一个 输入层 (仅 1 个节点),用它的后果作为咱们模型的最终预测。如下图所示
留神,这些中间层能够具备不同数量的神经元,应用不同的激活函数。每个神经元计算都会有对应的权重,因而增加的层数越多,可训练参数的数量就越大。
残缺的神经网络全貌如下图所示:
咱们方才是以口语化的形式来叙述整个神经网络的构造和计算过程的,但理论有一些细节点,包含:
- 偏置项bias:在每个神经元外部,输出和权重的线性组合也包含一个偏差,相似于线性方程中的常数,因而神经元的残缺公式是
来学习以最小化损失。
- 梯度降落:用于训练神经网络的优化算法,通过在最陡降落方向上反复步骤来找到损失函数的部分最小值。
模型搭建
咱们应用 TensorFlow 的 high level API(也就是 tensorflow.keras
)来疾速搭建神经网络 。
ShowMeAI制作了快捷即查即用的 Tensorflow 工具速查表手册,大家能够在下述地位取得:
- Tensorflow速查手册
咱们先搭建方才提到的最简略的感知器后果,它是一个只有一个 Dense 层的模型。 Dense层是最根本的层构造,是一个全连贯的构造。
model = models.Sequential(name="Perceptron", layers=[ layers.Dense( # 全连贯层 name="dense", input_dim=3, # 输出维度为3 units=1, # 1个节点 activation='linear' # 激活函数(这里是线性函数) ) ])model.summary()
model.summary
操作能够输入网络的构造和参数等信息。当前情况下,咱们只有 4 个(3 个权重和 1 个偏置项),所以它十分精简。
要阐明一点的是,如果咱们这里要应用阶跃函数作为激活函数,咱们须要本人定义(目前Tensorflow中的激活函数不蕴含这个咱们长期设置的函数)
import tensorflow as tf# 定义激活函数def binary_step_activation(x): # 如果x>0返回1否则返回0 return K.switch(x>0, tf.math.divide(x,x), tf.math.multiply(x,0))# 构建模型model = models.Sequential(name="Perceptron", layers=[ layers.Dense( name="dense", input_dim=3, units=1, activation=binary_step_activation )])
如果咱们从感知器转延展到深度神经网络,大家可能会冒出来一些问题,比方:
❓ 应该设置多少层?
- 这是一个没有标准答案的问题,隐层的退出对于模型的学习能力晋升有帮忙,能够拟合更简单的非线性状况,但也可能会导致过拟合(当然咱们能够通过 Dropout 等技术来缓解过拟合,对于 Dropout,大家能够浏览学习ShowMeAI的文章 深度学习教程 | 深度学习的实用层面。
- 一般来说,简略的问题咱们会用很少的层数(不超过3个隐层),简单的问题咱们应用的层数更多
下图是层数和学习能力的一个示意图。
❓ 应该设定多少个神经元?
- 这个有不同的设置办法,一个罕用的设置形式是 (输出维度 + 输入维度)/2 。
❓ 选用什么激活函数?
- 激活函数有很多抉择,其中没有哪一个肯定好于另外一个。最罕用的是 ReLU ,一个分段线性函数,仅在输入为正时才返回。
- 留神,在输入层必须具备与工作输入兼容的激活。例如,linear函数实用于回归问题,而 Sigmoid/softmax 常常用于分类。
咱们来解决一个二分类问题,它有 N 个输出特色和 1 个二进制指标变量。
n_features = 10model = models.Sequential(name="DeepNN", layers=[ # 第1个隐层 layers.Dense(name="h1", input_dim=n_features, units=int(round((n_features+1)/2)), activation='relu'), layers.Dropout(name="drop1", rate=0.2), # 第2个隐层 layers.Dense(name="h2", units=int(round((n_features+1)/4)), activation='relu'), layers.Dropout(name="drop2", rate=0.2), # 第3层 layers.Dense(name="output", units=1, activation='sigmoid')])model.summary()
除了这种汉堡包式地重叠神经网络档次构建网络的 Sequential 办法。tensorflow.keras 还有函数式编程构造,它可用于构建具备多个输出/输入的更简单的模型。函数式编程接口绝对 Sequential 有两个次要区别:
- 须要指定输出层,而在 Sequential 类中它隐含在第一个 Dense 层的输出维度中。
- 每一层能够间接利用于其余层的输入,形如: output = layer(...)(input)
咱们用函数式形式重写下面的网络,代码如下:
# 感知器inputs = layers.Input(name="input", shape=(3,))outputs = layers.Dense(name="output", units=1, activation='linear')(inputs)model = models.Model(inputs=inputs, outputs=outputs, name="Perceptron")# 深层神经网络# 输出层inputs = layers.Input(name="input", shape=(n_features,))# 隐层1h1 = layers.Dense(name="h1", units=int(round((n_features+1)/2)), activation='relu')(inputs)h1 = layers.Dropout(name="drop1", rate=0.2)(h1)# 隐层2h2 = layers.Dense(name="h2", units=int(round((n_features+1)/4)), activation='relu')(h1)h2 = layers.Dropout(name="drop2", rate=0.2)(h2)# 输入层outputs = layers.Dense(name="output", units=1, activation='sigmoid')(h2)# 残缺的模型model = models.Model(inputs=inputs, outputs=outputs, name="DeepNN")
神经网络构造可视化
这个局部是工具局部,咱们心愿通过一段代码把多层的神经网络的大抵构造绘制进去。当然,这里的构造只是一个简略的信息出现,如果大家要深刻了解神经网络的权重和激活函数等绘制,能够看ShowMeAI 后续的深刻教程。
残缺的代码如下:
'''抽取tensorflow.keras模型中的每层信息'''def utils_nn_config(model): lst_layers = [] if "Sequential" in str(model): #-> Sequential不显示输出层 layer = model.layers[0] lst_layers.append({"name":"input", "in":int(layer.input.shape[-1]), "neurons":0, "out":int(layer.input.shape[-1]), "activation":None, "params":0, "bias":0}) for layer in model.layers: try: dic_layer = {"name":layer.name, "in":int(layer.input.shape[-1]), "neurons":layer.units, "out":int(layer.output.shape[-1]), "activation":layer.get_config()["activation"], "params":layer.get_weights()[0], "bias":layer.get_weights()[1]} except: dic_layer = {"name":layer.name, "in":int(layer.input.shape[-1]), "neurons":0, "out":int(layer.output.shape[-1]), "activation":None, "params":0, "bias":0} lst_layers.append(dic_layer) return lst_layers'''绘制神经网络的草图'''def visualize_nn(model, description=False, figsize=(10,8)): # 获取档次信息 lst_layers = utils_nn_config(model) layer_sizes = [layer["out"] for layer in lst_layers] # 绘图设置 fig = plt.figure(figsize=figsize) ax = fig.gca() ax.set(title=model.name) ax.axis('off') left, right, bottom, top = 0.1, 0.9, 0.1, 0.9 x_space = (right-left) / float(len(layer_sizes)-1) y_space = (top-bottom) / float(max(layer_sizes)) p = 0.025 # 两头节点 for i,n in enumerate(layer_sizes): top_on_layer = y_space*(n-1)/2.0 + (top+bottom)/2.0 layer = lst_layers[i] color = "green" if i in [0, len(layer_sizes)-1] else "blue" color = "red" if (layer['neurons'] == 0) and (i > 0) else color ## 增加信息阐明 if (description is True): d = i if i == 0 else i-0.5 if layer['activation'] is None: plt.text(x=left+d*x_space, y=top, fontsize=10, color=color, s=layer["name"].upper()) else: plt.text(x=left+d*x_space, y=top, fontsize=10, color=color, s=layer["name"].upper()) plt.text(x=left+d*x_space, y=top-p, fontsize=10, color=color, s=layer['activation']+" (") plt.text(x=left+d*x_space, y=top-2*p, fontsize=10, color=color, s=""+str(layer['in'])+"[X*w]+b") out = " Y" if i == len(layer_sizes)-1 else " out" plt.text(x=left+d*x_space, y=top-3*p, fontsize=10, color=color, s=") = "+str(layer['neurons'])+out) ## 遍历 for m in range(n): color = "limegreen" if color == "green" else color circle = plt.Circle(xy=(left+i*x_space, top_on_layer-m*y_space-4*p), radius=y_space/4.0, color=color, ec='k', zorder=4) ax.add_artist(circle) ## 增加文本阐明 if i == 0: plt.text(x=left-4*p, y=top_on_layer-m*y_space-4*p, fontsize=10, s=r'$X_{'+str(m+1)+'}$') elif i == len(layer_sizes)-1: plt.text(x=right+4*p, y=top_on_layer-m*y_space-4*p, fontsize=10, s=r'$y_{'+str(m+1)+'}$') else: plt.text(x=left+i*x_space+p, y=top_on_layer-m*y_space+(y_space/8.+0.01*y_space)-4*p, fontsize=10, s=r'$H_{'+str(m+1)+'}$') # 增加链接箭头等 for i, (n_a, n_b) in enumerate(zip(layer_sizes[:-1], layer_sizes[1:])): layer = lst_layers[i+1] color = "green" if i == len(layer_sizes)-2 else "blue" color = "red" if layer['neurons'] == 0 else color layer_top_a = y_space*(n_a-1)/2. + (top+bottom)/2. -4*p layer_top_b = y_space*(n_b-1)/2. + (top+bottom)/2. -4*p for m in range(n_a): for o in range(n_b): line = plt.Line2D([i*x_space+left, (i+1)*x_space+left], [layer_top_a-m*y_space, layer_top_b-o*y_space], c=color, alpha=0.5) if layer['activation'] is None: if o == m: ax.add_artist(line) else: ax.add_artist(line) plt.show()
咱们在之前的 2 个模型上尝试一下,首先是感知器:
visualize_nn(model, description=True, figsize=(10,8))
而后是深度神经网络:
当然,TensorFlow 自身也提供了一个绘制模型构造的办法,它不是像上述示例图一样的简略模式出现,而是输入更多的模型档次信息,上面是咱们对深度模型调用 plot_model
的后果。
utils.plot_model(model, to_file='model.png', show_shapes=True, show_layer_names=True)
同时也会生成名为model.png
的图片保留在你本地笔记本电脑上。
训练和测试评估
下一步是训练咱们后面构建的深度学习模型。在 tensorflow.keras
中,咱们须要先对模型『编译』,或者换句话说,咱们须要定义训练过程中的一些细节,比方优化器Optimizer、损失函数Loss和评估准则Metrics。其中:
- 最罕用的优化器是 Adam,它是一种用于梯度降落的替换优化算法(自适应优化器中较好的)。对于不同的优化算法,大家能够浏览学习ShowMeAI的文章 深度学习教程 | 神经网络优化算法。
- 针对二分类问题,咱们个别是用 (二元)穿插熵 损失,它将每个预测概率与理论类输入进行比拟。对于损失函数,大家能够浏览ShowMeAI的文章 深度学习与CV教程(3) | 损失函数与最优化。
- 评估指标,咱们同时监控 Accuracy 和 F1-score, 这是一个联合了 Precision 和 Recall 的综合指标。对于评估准则,大家能够浏览ShowMeAI的文章 图解机器学习 | 模型评估办法与准则。
具体的编译代码如下:
# 定义评估准则def Recall(y_true, y_pred): true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1))) possible_positives = K.sum(K.round(K.clip(y_true, 0, 1))) recall = true_positives / (possible_positives + K.epsilon()) return recalldef Precision(y_true, y_pred): true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1))) predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1))) precision = true_positives / (predicted_positives + K.epsilon()) return precisiondef F1(y_true, y_pred): precision = Precision(y_true, y_pred) recall = Recall(y_true, y_pred) return 2*((precision*recall)/(precision+recall+K.epsilon())) # 编译神经网络model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy',F1])
咱们以后是分类问题,如果是回归问题,咱们能够选用 MAE 为损失,将 R方 作为度量。参考代码如下:
# 定义R方评估准则def R2(y, y_hat): ss_res = K.sum(K.square(y - y_hat)) ss_tot = K.sum(K.square(y - K.mean(y))) return ( 1 - ss_res/(ss_tot + K.epsilon()) ) # 编译神经网络model.compile(optimizer='adam', loss='mean_absolute_error', metrics=[R2])
神经网络的训练,大部分时候,不是一次性把数据都送入模型学习的(因为数据量十分大,通常GPU不足以包容这种规模的数据,同时全量数据也容易陷入部分最低点)
对于评估准则,大家能够浏览ShowMeAI的文章 深度学习教程 | 神经网络优化算法
咱们通常会采纳一个批次一个批次数据训练的形式,因而在开始训练之前,咱们还须要确定 Epochs 和 Batches:其中Epochs代表全量数据迭代的次数,Batches代表单个批次数据样本的数量。
总的数据会拆分为若干批次(每个batch的样本数量越大,您须要的内存空间越多),反向流传和参数更在每批数据上都会进行。一个Epoch是对整个训练集的一次遍历。
如果咱们手头有 100 个样本且batch大小为 20,则须要 5 个batch能力实现 1 个 epoch。batch大小尽量抉择为2的倍数(常见:32、64、128、256),因为计算机通常以 2 的幂来组织内存。
在训练过程中,现实的状态是随着一轮一轮的数据迭代,评估指标在不断改进,损失在逐渐缩小。不过这个后果只表明在训练集数据上咱们在学习,但在新数据上是否有同样的成果并不好确定。因而咱们会保留一部分数据(20%左右)用于验证评估。
咱们用代码来做阐明,咱们在这里简略地生成随机数据构建特色数据X和标签数据y,例如
import numpy as npX = np.random.rand(1000,10)y = np.random.choice([1,0], size=1000)
那咱们能够基于下述形式对数据进行训练和评估
# 训练和评估training = model.fit(x=X, y=y, batch_size=32, epochs=100, shuffle=True, verbose=0, validation_split=0.2)# 绘制评估指标metrics = [k for k in training.history.keys() if ("loss" not in k) and ("val" not in k)] fig, ax = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(15,3)) # 训练阶段 ax[0].set(title="Training") ax11 = ax[0].twinx() ax[0].plot(training.history['loss'], color='black') ax[0].set_xlabel('Epochs') ax[0].set_ylabel('Loss', color='black') for metric in metrics: ax11.plot(training.history[metric], label=metric) ax11.set_ylabel("Score", color='steelblue') ax11.legend() # 验证集评估阶段ax[1].set(title="Validation") ax22 = ax[1].twinx() ax[1].plot(training.history['val_loss'], color='black') ax[1].set_xlabel('Epochs') ax[1].set_ylabel('Loss', color='black') for metric in metrics: ax22.plot(training.history['val_'+metric], label=metric) ax22.set_ylabel("Score", color="steelblue") plt.show()
失去的后果图如下所示(下2幅图别离为分类和回归场景下的训练集与验证集的loss和评估准则指标):
模型可解释性
理论生产过程中,神经网络成果可能很好,但咱们理论是不太方向间接把它当做一个黑盒来用的,咱们心愿对模型做一些可解释性剖析,能局部地了解咱们的模型。
咱们在这里会用到一个模型可解释性工具 Shap ,用它和神经网络搭配对模型做一些解释。
具体说来,对于每个样本的预测,咱们联合shap都可能预计每个特色对模型预测后果的奉献,进而局部解释问模型的问题『为什么预测这是 1 而不是 0?』(二分类场景)。
参考代码如下:
'''应用shap构建解释器:parameter :param model: model instance (after fitting) :param X_names: list :param X_instance: array of size n x 1 (n,) :param X_train: array - if None the model is simple machine learning, if not None then it's a deep learning model :param task: string - "classification", "regression" :param top: num - top features to display:return dtf with explanations'''def explainer_shap(model, X_names, X_instance, X_train=None, task="classification", top=10): # 构建解释器 # 机器学习(树模型) if X_train is None: explainer = shap.TreeExplainer(model) shap_values = explainer.shap_values(X_instance) # 深度学习(神经网络) else: explainer = shap.DeepExplainer(model, data=X_train[:100]) shap_values = explainer.shap_values(X_instance.reshape(1,-1))[0].reshape(-1) # 绘图 # 分类场景 if task == "classification": shap.decision_plot(explainer.expected_value, shap_values, link='logit', feature_order='importance', features=X_instance, feature_names=X_names, feature_display_range=slice(-1,-top-1,-1)) # 回归场景 else: shap.waterfall_plot(explainer.expected_value[0], shap_values, features=X_instance, feature_names=X_names, max_display=top)
shap实际上是一个很无效的工具,大家在上述代码也能够看到,理论它能够利用在机器学习模型(如线性回归、随机森林)上,也能够利用在神经网络上。从代码中能够看出,如果 X_train 参数为 None,会抉择机器学习模型进行解释,否则应用深度学习进行解释剖析。
咱们在Titanic分类问题和房价预估回归问题上进行测试:(对应的这两个案例大家能够在ShowMeAI后续的文章中找到)
i = 1explainer_shap(model, X_names=list_feature_names, X_instance=X[i], X_train=X, task="classification", # 分类工作 top=10)
上图中,Titanic问题中,预测为『Survived』次要因素是变量 Sex_male = 0,即乘客是女性。
上图中,在房价预估的回归问题中,影响最大的因素是屋宇的面积。
参考资料
- 深度学习教程 | 吴恩达专项课程 · 全套笔记解读: https://www.showmeai.tech/tutorials/35
- 自然语言解决教程 | 斯坦福CS224n课程 · 课程带学与全套笔记解读: https://www.showmeai.tech/tutorials/36
- 深度学习与计算机视觉教程:斯坦福CS231n · 全套笔记解读: https://www.showmeai.tech/tutorials/37
- 深度学习教程 | 神经网络根底: https://www.showmeai.tech/article-detail/213
- 深度学习教程 | 浅层神经网络: https://www.showmeai.tech/article-detail/214
- 深度学习教程 | 深层神经网络: https://www.showmeai.tech/article-detail/215
- 深度学习教程 | 深度学习的实用层面: https://www.showmeai.tech/article-detail/216
- 深度学习教程 | 神经网络优化算法: https://www.showmeai.tech/article-detail/217
- 深度学习与CV教程 | 损失函数与最优化: https://www.showmeai.tech/article-detail/262
- 图解机器学习 | 模型评估办法与准则: https://www.showmeai.tech/article-detail/186
- TensorFlow速查手册: https://www.showmeai.tech/article-detail/109
- Matplotlib速查手册: https://www.showmeai.tech/article-detail/103