持续创造,加快生长!这是我参加「日新方案 6 月更文应战」的第5天,点击检查活动概况
- 作者:韩信子@ShowMeAI
- 教程地址:www.showmeai.tech/tutorials/3…
- 本文地址:www.showmeai.tech/article-det…
- 声明:版权一切,转载请联系渠道与作者并注明出处
- 保藏ShowMeAI检查更多精彩内容
本系列为 斯坦福CS231n 《深度学习与核算机视觉(Deep Learning for Computer Vision)》的全套学习笔记,对应的课程视频能够在 这儿 检查。更多材料获取办法见文末。
导言
大家在前序文章中学习了许多关于神经网络的原理常识和实战技巧,在本篇内容中ShowMeAI给大家打开介绍深度学习硬件常识,以及现在主流的深度学习结构TensorFlow和pytorch相关常识,借助于东西大家能够实践建立与练习神经网络。
本篇关键
-
深度学习硬件
- CPU、GPU、TPU
-
深度学习结构
- PyTorch / TensorFlow
- 静态与动态核算图
1.深度学习硬件
GPU(Graphics Processing Unit)是图形处理单元(又称显卡),在物理尺度上就比 CPU(Central Processing Unit)大得多,有自己的冷却系统。开始用于烘托核算机图形,尤其是游戏。在深度学习上挑选 NVIDIA(英伟达)的显卡,假如运用AMD的显卡会遇到许多问题。TPU(Tensor Processing Units)是专用的深度学习硬件。
1.1 CPU / GPU / TPU
- CPU一般有多个中心,每个中心速度都很快都能够独立作业,可一起进行多个进程,内存与系统同享,完结序列任务时很有用。图上CPU的运转速度是每秒约 540 GFLOPs 浮点数运算,运用 32 位浮点数(注:一个 GFLOPS(gigaFLOPS)等于每秒十亿(=109=10^9)次的浮点运算)。
- GPU有上千个中心数,但每个中心运转速度很慢,也不能独立作业,合适大量的并行完结相似的作业。GPU一般自带内存,也有自己的缓存系统。图上GPU的运转速度是CPU的20多倍。
- TPU是专门的深度学习硬件,运转速度十分快。TITANV 在技能上并不是一个「TPU」,因为这是一个谷歌术语,但两者都有专门用于深度学习的硬件。运转速度十分快。
若是将这些运转速度除以对应的价格,可得到下图:
1.2 GPU的优势与运用
GPU 在大矩阵的乘法运算中有很明显的优势。
因为成果中的每一个元素都是相乘的两个矩阵的每一行和每一列的点积,所以并行的一起进行这些点积运算速度会十分快。卷积神经网络也相似,卷积核和图片的每个区域进行点积也是并行运算。
CPU 尽管也有多个中心,可是在大矩阵运算时只能串行运算,速度很慢。
能够写出在 GPU 上直接运转的代码,办法是运用NVIDIA自带的抽象代码 CUDA ,能够写出相似 C 的代码,并能够在 GPU 直接运转。
可是直接写 CUDA 代码是一件十分困难的事,好在能够直接运用 NVIDIA 现已高度优化而且开源的API,比方 cuBLAS 包括许多矩阵运算, cuDNN 包括 CNN 前向传达、反向传达、批量归一化等操作;还有一种言语是 OpenCL,能够在 CPU、AMD 上通用,可是没人做优化,速度很慢;HIP能够将CUDA 代码主动转换成能够在 AMD 上运转的言语。以后或许会有跨渠道的规范,可是现在来看 CUDA 是最好的挑选。
在实践运用中,同样的核算任务,GPU 比 CPU 要快得多,当然 CPU 还能进一步优化。运用 cuDNN 也比不运用要快挨近三倍。
实践运用 GPU 还有一个问题是练习的模型一般存放在 GPU,而用于练习的数据存放在硬盘里,因为 GPU 运转快,而机械硬盘读取慢,就会拖累整个模型的练习速度。有多种处理办法:
- 假如练习数据数量较小,能够把一切数据放到 GPU 的 RAM 中;
- 用固态硬盘替代机械硬盘;
- 运用多个 CPU 线程预读取数据,放到缓存供 GPU 运用。
2.深度学习软件
2.1 DL软件概述
现在有许多种深度学习结构,现在最流行的是 TensorFlow。
第一代结构大多由学术界编写的,比方 Caffe 便是伯克利大学开发的。
第二代往往由工业界主导,比方 Caffe2 是由 Facebook 开发。这儿首要解说 PyTorch 和 TensorFlow。
回顾之前核算图的概念,一个线性分类器能够用核算图表明,网络越杂乱,核算图也越杂乱。之所以运用这些深度学习结构有三个原因:
- 构建大的核算图很容易,能够快速的开发和测验新想法;
- 这些结构都能够主动核算梯度只需写出前向传达的代码;
- 能够在 GPU 上高效的运转,现已扩展了 cuDNN 等包以及处理好数据如安在 CPU 和 GPU 中流动。
这样咱们就不必从头开始完结这些作业了。
比方下面的一个核算图:
咱们以前的做法是运用 Numpy 写出前向传达,然后核算梯度,代码如下:
import numpy as np
np.random.seed(0) # 确保每次的随机数共同
N, D = 3, 4
x = np.random.randn(N, D)
y = np.random.randn(N, D)
z = np.random.randn(N, D)
a = x * y
b = a + z
c = np.sum(b)
grad_c = 1.0
grad_b = grad_c * np.ones((N, D))
grad_a = grad_b.copy()
grad_z = grad_b.copy()
grad_x = grad_a * y
grad_y = grad_a * x
这种做法 API 干净,易于编写代码,但问题是没办法在 GPU 上运转,而且需求自己核算梯度。所以现在大部分深度学习结构的首要方针是自己写好前向传达代码,相似 Numpy,但能在 GPU 上运转且能够主动核算梯度。
TensorFlow 版别,前向传达构建核算图,梯度能够主动核算:
import numpy as np
np.random.seed(0)
import tensorflow as tf
N, D = 3, 4
# 创立前向核算图
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
z = tf.placeholder(tf.float32)
a = x * y
b = a + z
c = tf.reduce_sum(b)
# 核算梯度
grad_x, grad_y, grad_z = tf.gradients(c, [x, y, z])
with tf.Session() as sess:
values = {
x: np.random.randn(N, D),
y: np.random.randn(N, D),
z: np.random.randn(N, D),
}
out = sess.run([c, grad_x, grad_y, grad_z], feed_dict=values)
c_val, grad_x_val, grad_y_val, grad_z_val = out
print(c_val)
print(grad_x_val)
PyTorch版别,前向传达与Numpy十分相似,但反向传达能够主动核算梯度,不必再去完成。
import torch
device = 'cuda:0' # 在GPU上运转,即构建GPU版别的矩阵
# 前向传达与Numpy相似
N, D = 3, 4
x = torch.randn(N, D, requires_grad=True, device=device)
# requires_grad要求主动核算梯度,默认为True
y = torch.randn(N, D, device=device)
z = torch.randn(N, D, device=device)
a = x * y
b = a + z
c = torch.sum(b)
c.backward() # 反向传达能够主动核算梯度
print(x.grad)
print(y.grad)
print(z.grad)
可见这些结构都能主动核算梯度而且能够主动在 GPU 上运转。
2.2 TensoFlow
关于TensorFlow的用法也能够阅读ShowMeAI的制作的 TensorFlow 速查表,对应文章AI 建模东西速查 | TensorFlow运用指南和AI建模东西速查 | Keras运用指南。
下面以一个两层的神经网络为例,非线性函数运用 ReLU 函数、丢失函数运用 L2 范式(当然仅仅是一个学习示例)。
完成代码如下:
1) 神经网络
import numpy as np
import tensorflow as tf
N, D , H = 64, 1000, 100
# 创立前向核算图
x = tf.placeholder(tf.float32, shape=(N, D))
y = tf.placeholder(tf.float32, shape=(N, D))
w1 = tf.placeholder(tf.float32, shape=(D, H))
w2 = tf.placeholder(tf.float32, shape=(H, D))
h = tf.maximum(tf.matmul(x, w1), 0) # 躲藏层运用折叶函数
y_pred = tf.matmul(h, w2)
diff = y_pred - y # 差值矩阵
loss = tf.reduce_mean(tf.reduce_sum(diff ** 2, axis=1)) # 丢失函数运用L2范数
# 核算梯度
grad_w1, grad_w2 = tf.gradients(loss, [w1, w2])
# 屡次运转核算图
with tf.Session() as sess:
values = {
x: np.random.randn(N, D),
y: np.random.randn(N, D),
w1: np.random.randn(D, H),
w2: np.random.randn(H, D),
}
out = sess.run([loss, grad_w1, grad_w2], feed_dict=values)
loss_val, grad_w1_val, grad_w2_val = out
整个进程能够分成两部分,with
之前部分界说核算图,with
部分屡次运转核算图。这种形式在TensorFlow 中很常见。
- 首要,咱们创立了
x,y,w1,w2
四个tf.placeholder
方针,这四个变量作为「输入槽」,下面再输入数据。 - 然后运用这四个变量创立核算图,运用矩阵乘法
tf.matmul
和折叶函数tf.maximum
核算y_pred
,运用 L2 距离核算 s 丢失。可是现在并没有实践的核算,因为仅仅构建了核算图并没有输入任何数据。 - 然后通过一行奇特的代码核算丢失值关于
w1
和w2
的梯度。此刻依然没有实践的运算,仅仅构建核算图,找到 loss 关于w1
和w2
的途径,在原先的核算图上增加额定的关于梯度的核算。 - 完结核算图后,创立一个会话 Session 来运转核算图和输入数据。进入到 Session 后,需求供给 Numpy 数组给上面创立的「输入槽」。
- 最终两行代码才是真实的运转,履行
sess.run
需求供给 Numpy 数组字典feed_dict和需求输出的核算值 loss ,
grad_w1,
grad_w2` ,最终通过解包获取 Numpy 数组。
上面的代码仅仅运转了一次,咱们需求迭代屡次,并设置超参数、参数更新办法等:
with tf.Session() as sess:
values = {
x: np.random.randn(N, D),
y: np.random.randn(N, D),
w1: np.random.randn(D, H),
w2: np.random.randn(H, D),
}
learning_rate = 1e-5
for t in range(50):
out = sess.run([loss, grad_w1, grad_w2], feed_dict=values)
loss_val, grad_w1_val, grad_w2_val = out
values[w1] -= learning_rate * grad_w1_val
values[w2] -= learning_rate * grad_w2_val
这种迭代办法有一个问题是每一步需求将Numpy和数组供给给GPU,GPU核算完结后再解包成Numpy数组,但因为CPU与GPU之间的传输瓶颈,十分不方便。
处理办法是将 w1
和 w2
作为变量而不再是「输入槽」,变量能够一直存在于核算图上。
因为现在 w1
和 w2
变成了变量,所以就不能从外部输入 Numpy 数组来初始化,需求由 TensorFlow 来初始化,需求指明初始化办法。此刻依然没有详细的核算。
w1 = tf.Variable(tf.random_normal((D, H)))
w2 = tf.Variable(tf.random_normal((H, D)))
现在需求将参数更新操作也增加到核算图中,运用赋值操作 assign
更新 w1
和 w2
,并保存在核算图中(坐落核算梯度后边):
learning_rate = 1e-5
new_w1 = w1.assign(w1 - learning_rate * grad_w1)
new_w2 = w2.assign(w2 - learning_rate * grad_w2)
现在运转这个网络,需求先运转一步参数的初始化 tf.global_variables_initializer()
,然后运转屡次代码核算丢失值:
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
values = {
x: np.random.randn(N, D),
y: np.random.randn(N, D),
}
for t in range(50):
loss_val, = sess.run([loss], feed_dict=values)
2) 优化器
上面的代码,实践练习进程中丢失值不会变。
原因是咱们履行的 sess.run([loss], feed_dict=values)
语句只会核算 loss
,TensorFlow 十分高效,与丢失值无关的核算一概不会进行,所以参数就无法更新。
一个处理办法是在履行 run
时参加核算两个参数,这样就会强制履行参数更新,可是又会发生CPU 与 GPU 的通讯问题。
一个技巧是在核算图中参加两个参数的依靠,在履行时需求核算这个依靠,这样就会让参数更新。这个技巧是 group
操作,履行完参数赋值操作后,履行 updates = tf.group(new_w1, new_w2)
,这个操作会在核算图上创立一个节点;然后履行的代码修正为 loss_val, _ = sess.run([loss, updates], feed_dict=values)
,在实践运算时,updates
返回值为空。
这种办法依然不够方便,好在 TensorFlow 供给了更快捷的操作,运用自带的优化器。优化器需求供给学习率参数,然后进行参数更新。有许多优化器可供挑选,比方梯度下降、Adam等。
optimizer = tf.train.GradientDescentOptimizer(1e-5) # 运用优化器
updates = optimizer.minimize(loss) # 更新办法是使loss下降,内部其实运用了group
履行的代码也是:loss_val, _ = sess.run([loss, updates], feed_dict=values)
3) 丢失
核算丢失的代码也能够运用 TensorFlow 自带的函数:
loss = tf.losses.mean_squared_error(y_pred, y) # 丢失函数运用L2范数
4) 层
现在仍有一个很大的问题是 x,y,w1,w2
的形状需求咱们自己去界说,还要确保它们能正确衔接在一起,此外还有误差。假如运用卷积层、批量归一化等层后,这些界说会愈加费事。
TensorFlow能够处理这些费事:
N, D , H = 64, 1000, 100
x = tf.placeholder(tf.float32, shape=(N, D))
y = tf.placeholder(tf.float32, shape=(N, D))
init = tf.variance_scaling_initializer(2.0) # 权重初始化运用He初始化
h = tf.layers.dense(inputs=x, units=H, activation=tf.nn.relu, kernel_initializer=init)
# 躲藏层运用折叶函数
y_pred = tf.layers.dense(inputs=h, units=D, kernel_initializer=init)
loss = tf.losses.mean_squared_error(y_pred, y) # 丢失函数运用L2范数
optimizer = tf.train.GradientDescentOptimizer(1e-5)
updates = optimizer.minimize(loss)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
values = {
x: np.random.randn(N, D),
y: np.random.randn(N, D),
}
for t in range(50):
loss_val, _ = sess.run([loss, updates], feed_dict=values)
上面的代码,x,y
的初始化没有变化,可是参数 w1,w2
躲藏起来了,初始化运用 He初始化。
前向传达的核算运用了全衔接层 tf.layers.dense
,该函数需求供给输入数据 inputs
、该层的神经元数目 units
、激活函数 activation
、卷积核(权重)初始化办法 kernel_initializer
等参数,能够主动设置权重和误差。
5) High level API:tensorflow.keras
Keras 是根据 TensorFlow 的更高层次的封装,会让整个进程变得简略,曾经是第三方库,现在现已被内置到了 TensorFlow。
运用 Keras 的部分代码如下,其他与上文共同:
N, D , H = 64, 1000, 100
x = tf.placeholder(tf.float32, shape=(N, D))
y = tf.placeholder(tf.float32, shape=(N, D))
model = tf.keras.Sequential() # 运用一系列层的组合办法
# 增加一系列的层
model.add(tf.keras.layers.Dense(units=H, input_shape=(D,), activation=tf.nn.relu))
model.add(tf.keras.layers.Dense(D))
# 调用模型获取成果
y_pred = model(x)
loss = tf.losses.mean_squared_error(y_pred, y)
这种模型现已简化了许多作业,最终版别代码如下:
import numpy as np
import tensorflow as tf
N, D , H = 64, 1000, 100
# 创立模型,增加层
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(units=H, input_shape=(D,), activation=tf.nn.relu))
model.add(tf.keras.layers.Dense(D))
# 装备模型:丢失函数、参数更新办法
model.compile(optimizer=tf.keras.optimizers.SGD(lr=1e-5), loss=tf.keras.losses.mean_squared_error)
x = np.random.randn(N, D)
y = np.random.randn(N, D)
# 练习
history = model.fit(x, y, epochs=50, batch_size=N)
代码十分简练:
-
界说模型:
tf.keras.Sequential()
表明模型是一系列的层,然后增加两个全衔接层,并设置激活函数、每层的神经元数目等; -
装备模型:用
model.compile
办法装备模型的优化器、丢失函数等; -
根据数据练习模型:运用
model.fit
,需求设置迭代周期次数、批量数等,能够直接用原始数据练习模型。
6) 其他常识
① 常见的拓宽包
- Keras (keras.io/)
- TensorFlow内置:
- tf.keras (www.tensorflow.org/api_docs/py…)
- tf.layers (www.tensorflow.org/api_docs/py…)
- tf.estimator (www.tensorflow.org/api_docs/py…)
- tf.contrib.estimator (www.tensorflow.org/api_docs/py…)
- tf.contrib.layers (www.tensorflow.org/api_docs/py…)
- tf.contrib.slim (github.com/tensorflow/…)
- tf.contrib.learn (www.tensorflow.org/api_docs/py…) (弃用)
- Sonnet (github.com/deepmind/so…) (by DeepMind)
- 第三方包:
- TFLearn (tflearn.org/)
- TensorLayer (tensorlayer.readthedocs.io/en/latest/) TensorFlow: High-Level
② 预练习模型
TensorFlow现已有一些预练习好的模型能够直接拿来用,运用迁移学习,微调参数。
- tf.keras: (www.tensorflow.org/api_docs/py…)
- TF-Slim: (github.com/tensorflow/…)
③ Tensorboard
- 增加日志记载丢失值和状态
- 绘制图画
④ 分布式操作
能够在多台机器上运转,谷歌比较拿手。
⑤ TPU(Tensor Processing Units)
TPU是专用的深度学习硬件,运转速度十分快。Google Cloud TPU 算力为180 TFLOPs ,NVIDIA Tesla V100算力为125 TFLOPs。
⑥Theano
TensorFlow的前身,二者许多地方都很相似。
2.3 PyTorch
关于PyTorch的用法也能够阅读ShowMeAI的制作的PyTorch速查表,对应文章AI 建模东西速查 | Pytorch运用指南
1) 基本概念
- Tensor:与Numpy数组很相似,仅仅能够在GPU上运转;
- Autograd:运用Tensors构建核算图并主动核算梯度的包;
- Module:神经网络的层,能够存储状态和可学习的权重。
下面的代码运用的是v0.4版别。
2) Tensors
下面运用Tensors练习一个两层的神经网络,激活函数运用ReLU、丢失运用L2丢失。
代码如下:
import torch
# cpu版别
device = torch.device('cpu')
#device = torch.device('cuda:0') # 运用gpu
# 为数据和参数创立随机的Tensors
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in, device=device)
y = torch.randn(N, D_out, device=device)
w1 = torch.randn(D_in, H, device=device)
w2 = torch.randn(H, D_out, device=device)
learning_rate = 1e-6
for t in range(500):
# 前向传达,核算猜测值和丢失
h = x.mm(w1)
h_relu = h.clamp(min=0)
y_pred = h_relu.mm(w2)
loss = (y_pred - y).pow(2).sum()
# 反向传达手动核算梯度
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = h_relu.t().mm(grad_y_pred)
grad_h_relu = grad_y_pred.mm(w2.t())
grad_h = grad_h_relu.clone()
grad_h[h < 0] = 0
grad_w1 = x.t().mm(grad_h)
# 梯度下降,参数更新
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
- 首要创立
x,y,w1,w2
的随机 tensor,与 Numpy 数组的方式共同 - 然后前向传达核算丢失值和猜测值
- 然后手动核算梯度
- 最终更新参数
上述代码很简略,和 Numpy 版别的写法很挨近。可是需求手动核算梯度。
3) Autograd主动梯度核算
PyTorch 能够主动核算梯度:
import torch
# 创立随机tensors
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
w1 = torch.randn(D_in, H, requires_grad=True)
w2 = torch.randn(H, D_out, requires_grad=True)
learning_rate = 1e-6
for t in range(500):
# 前向传达
y_pred = x.mm(w1).clamp(min=0).mm(w2)
loss = (y_pred - y).pow(2).sum()
# 反向传达
loss.backward()
# 参数更新
with torch.no_grad():
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
w1.grad.zero_()
w2.grad.zero_()
与上一版代码的首要区别是:
- 创立
w1,w2
时要求requires_grad=True
,这样会主动核算梯度,并创立核算图。x1,x2
不需求核算梯度。 - 前向传达与之前的相似,但现在不必保存节点,PyTorch 能够帮助咱们盯梢核算图。
- 运用
loss.backward()
主动核算要求的梯度。 - 按步对权重进行更新,然后将梯度归零。
Torch.no_grad
的意思是「不要为这部分构建核算图」。以下划线结尾的 PyTorch 办法是就地修正 Tensor,不返回新的 Tensor。
TensorFlow 与 PyTorch 的区别是 TensorFlow 需求先显式的构造一个核算图,然后重复运转;PyTorch 每次做前向传达时都要构建一个新的图,使程序看起来愈加简练。
PyTorch 支撑界说自己的主动核算梯度函数,需求编写 forward
,backward
函数。与作业中很相似。能够直接用到核算图上,可是实践上自己界说的时候并不多。
4) NN
与 Keras 相似的高层次封装,会使整个代码变得简略。
import torch
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
# 界说模型
model = torch.nn.Sequential(torch.nn.Linear(D_in, H),
torch.nn.ReLu(),
torch.nn.Linear(H, D_out))
learning_rate = 1e-2
for t in range(500):
# 前向传达
y_pred = model(x)
loss = torch.nn.functional.mse_loss(y_pred, y)
# 核算梯度
loss.backward()
with torch.no_grad():
for param in model.parameters():
param -= learning_rate * param.grad
model.zero_grad()
- 界说模型是一系列的层组合,在模型中界说了层方针比方全衔接层、折叶层等,里边包括可学习的权重;
- 前向传达将数据给模型就能够直接核算猜测值,从而核算丢失;
torch.nn.functional
含有许多有用的函数,比方丢失函数; - 反向传达会核算模型中一切权重的梯度;
- 最终每一步都更新模型的参数。
5) Optimizer
PyTorch 同样有自己的优化器:
import torch
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
# 界说模型
model = torch.nn.Sequential(torch.nn.Linear(D_in, H),
torch.nn.ReLu(),
torch.nn.Linear(H, D_out))
# 界说优化器
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
# 迭代
for t in range(500):
y_pred = model(x)
loss = torch.nn.functional.mse_loss(y_pred, y)
loss.backward()
# 更新参数
optimizer.step()
optimizer.zero_grad()
- 运用不同规则的优化器,这儿运用Adam;
- 核算完梯度后,运用优化器更新参数,再置零梯度。
6) 界说新的模块
PyTorch 中一个模块便是一个神经网络层,输入和输出都是 tensors。模块中能够包括权重和其他模块,能够运用 Autograd 界说自己的模块。
比方能够把上面代码中的两层神经网络改成一个模块:
import torch
# 界说上文的整个模块为单个模块
class TwoLayerNet(torch.nn.Module):
# 初始化两个子模块,都是线性层
def __init__(self, D_in, H, D_out):
super(TwoLayerNet, self).__init__()
self.linear1 = torch.nn.Linear(D_in, H)
self.linear2 = torch.nn.Linear(H, D_out)
# 运用子模块界说前向传达,不需求界说反向传达,autograd会主动处理
def forward(self, x):
h_relu = self.linear1(x).clamp(min=0)
y_pred = self.linear2(h_relu)
return y_pred
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
# 构建模型与练习和之前相似
model = TwoLayerNet(D_in, H, D_out)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
for t in range(500):
y_pred = model(x)
loss = torch.nn.functional.mse_loss(y_pred, y)
loss.backward()
optimizer.step()
optimizer.zero_grad()
这种混合自界说模块十分常见,界说一个模块子类,然后作为作为整个模型的一部分增加到模块序列中。
比方用界说一个下面这样的模块,输入数据先通过两个并排的全衔接层得到的成果相乘后通过 ReLU:
class ParallelBlock(torch.nn.Module):
def __init__(self, D_in, D_out):
super(ParallelBlock, self).__init__()
self.linear1 = torch.nn.Linear(D_in, D_out)
self.linear2 = torch.nn.Linear(D_in, D_out)
def forward(self, x):
h1 = self.linear1(x)
h2 = self.linear2(x)
return (h1 * h2).clamp(min=0)
然后在整个模型中运用:
model = torch.nn.Sequential(ParallelBlock(D_in, H),
ParallelBlock(H, H),
torch.nn.Linear(H, D_out))
运用 ParallelBlock
的新模型核算图如下:
7) DataLoader
DataLoader 包装数据集并供给获取小批量数据,重新排列,多线程读取等,当需求加载自界说数据时,只需编写自己的数据集类:
import torch
from torch.utils.data import TensorDataset, DataLoader
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
loader = DataLoader(TensorDataset(x, y), batch_size=8)
model = TwoLayerNet(D_in, H, D_out)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-2)
for epoch in range(20):
for x_batch, y_batch in loader:
y_pred = model(x_batch)
loss = torch.nn.functional.mse_loss(y_pred, y_batch)
loss.backward()
optimizer.step()
optimizer.zero_grad()
上面的代码依然是两层神经完网络,运用了自界说的模块。这次运用了 DataLoader 来处理数据。最终更新的时候在小批量上更新,一个周期会迭代一切的小批量数据。一般的 PyTorch 模型基本都长成这个样子。
8) 预练习模型
运用预练习模型十分简略:github.com/pytorch/vis…
import torch
import torchvision
alexnet = torchvision.models.alexnet(pretrained=True)
vgg16 = torchvision.models.vggl6(pretrained=-True)
resnet101 = torchvision.models.resnet101(pretrained=True)
9) Visdom
可视化的包,相似 TensorBoard,可是不能像 TensorBoard 相同可视化核算图。
10) Torch
PyTorch 的前身,不能运用 Python,没有 Autograd,但比较稳定,不引荐运用。
3.静态与动态图(Static vs Dynamic Graphs )
TensorFlow运用的是静态图(Static Graphs):
- 构建核算图描绘核算,包括找到反向传达的途径;
- 每次迭代履行核算,都运用同一张核算图。
与静态图相对应的是PyTorch运用的动态图(Dynamic Graphs),构建核算图与核算一起进行:
- 创立tensor方针;
- 每一次迭代构建核算图数据结构、寻觅参数梯度途径、履行核算;
- 每一次迭代抛出核算图,然后再重建。之后重复上一步。
3.1 静态图的优势
运用静态图形,因为一张图需求反复运转许屡次,这样结构就有机会在核算图上做优化。
- 比方下面的自己写的核算图或许通过屡次运转后优化成右侧,进步运转效率。
静态图只需求构建一次核算图,所以一旦构建好了即便源代码运用 Python 写的,也能够布置在C++上,不必依靠源代码;而动态图每次迭代都要运用源代码,构件图和运转是交错在一起的。
3.2 动态图的优势
动态图的代码比较简练,很像 Python 操作。
在条件判断逻辑中,因为 PyTorch 能够动态构建图,所以能够运用正常的 Python 流操作;而TensorFlow 只能一次性构建一个核算图,所以需求考虑到一切情况,只能运用 TensorFlow 流操作,这儿运用的是和条件有关的。
在循环结构中,也是如此。
- PyTorch 只需依照 Python 的逻辑去写,每次会更新核算图而不必管最终的序列有多长;
- TensorFlow 因为运用静态图有必要把这个循环结构显示的作为节点增加到核算图中,所以需求用到 TensorFlow 的循环流
tf.foldl
。而且大多数情况下,为了确保只构建一次循环图, TensorFlow 只能运用自己的操控流,比方循环流、条件流等,而不能运用 Python 语法,所以用起来需求学习 TensorFlow 特有的操控指令。
3.3 动态图的运用
1) 循环网络(Recurrent Networks)
例如图画描绘,需求运用循环网络在一个不同长度序列上运转,咱们要生成的用于描绘图画的语句是一个序列,依靠于输入数据的序列,即动态的取决于输入语句的长短。
2) 递归网络(Recursive Networks)
用于自然言语处理,递归练习整个语法解析树,所以不仅仅是层次结构,而是一种图或树结构,在每个不同的数据点都有不同的结构,运用TensorFlow很难完成。在 PyTorch 中能够运用 Python 操控流,很容易完成。
3) Modular Networks
一种用于问询图片上的内容的网络,问题不相同生成的动态图也就不相同。
3.4 TensorFlow与PyTorch的彼此挨近
TensorFlow 与 PyTorch 的边界越来越模糊,PyTorch 正在增加静态功能,而 TensorFlow 正在增加动态功能。
- TensorFlow Fold 能够把静态图的代码主动转化成静态图
- TensorFlow 1.7增加了Eager Execution,答应运用动态图
import tensorflow as tf
import tensorflow.contrib.eager as tfe
tf.enable eager _execution()
N, D = 3, 4
x = tfe.Variable(tf.random_normal((N, D)))
y = tfe.Variable(tf.random_normal((N, D)))
z = tfe.Variable(tf.random_normal((N, D)))
with tfe.GradientTape() as tape:
a=x * 2
b=a + z
c = tf.reduce_sum(b)
grad_x, grad_y, grad_z = tape.gradient(c, [x, y, 2])
print(grad_x)
- 在程序开始时运用
tf.enable_eager_execution
形式:它是一个全局开关 -
tf.random_normal
会发生详细的值,无需 placeholders / sessions,假如想要为它们核算梯度,要用tfe.Variable进行包装 - 在
GradientTape
下操作将构建一个动态图,相似于 PyTorch - 运用
tape
核算梯度,相似 PyTorch 中的backward
。而且能够直接打印出来 - 静态的 PyTorch 有 [Caffe2](caffe2.ai/)、[[ONNX Support](github.com/onnx/onnx)]…
4.拓宽学习
能够点击 B站 检查视频的【双语字幕】版别
- 【课程学习指南】斯坦福CS231n | 深度学习与核算机视觉
- 【字幕+材料下载】斯坦福CS231n | 深度学习与核算机视觉 (2017全16讲)
- 【CS231n进阶课】密歇根EECS498 | 深度学习与核算机视觉
- 【深度学习教程】吴恩达专项课程 全套笔记解读
- 【Stanford官网】CS231n: Deep Learning for Computer Vision
5.关键总结
- 深度学习硬件最好运用 GPU,然后需求处理 CPU 与 GPU 的通讯问题。TPU 是专门用于深度学习的硬件,速度十分快。
- PyTorch 与 TensorFlow 都是十分好的深度学习结构,都有能够在 GPU 上直接运转的数组,都能够主动核算梯度,都有许多现已写好的函数、层等能够直接运用。前者运用动态图,后者运用静态图,不过二者都在向对方发展。取舍取决于项目。
ShowMeAI 斯坦福 CS231n 全套解读
- 深度学习与核算机视觉教程(1) | CV导言与根底 @CS231n
- 深度学习与核算机视觉教程(2) | 图画分类与机器学习根底 @CS231n
- 深度学习与核算机视觉教程(3) | 丢失函数与最优化 @CS231n
- 深度学习与核算机视觉教程(4) | 神经网络与反向传达 @CS231n
- 深度学习与核算机视觉教程(5) | 卷积神经网络 @CS231n
- 深度学习与核算机视觉教程(6) | 神经网络练习技巧 (上) @CS231n
- 深度学习与核算机视觉教程(7) | 神经网络练习技巧 (下) @CS231n
- 深度学习与核算机视觉教程(8) | 常见深度学习结构介绍 @CS231n
- 深度学习与核算机视觉教程(9) | 典型CNN架构 (Alexnet, VGG, Googlenet, Restnet等) @CS231n
- 深度学习与核算机视觉教程(10) | 轻量化CNN架构 (SqueezeNet, ShuffleNet, MobileNet等) @CS231n
- 深度学习与核算机视觉教程(11) | 循环神经网络及视觉运用 @CS231n
- 深度学习与核算机视觉教程(12) | 方针检测 (两阶段, R-CNN系列) @CS231n
- 深度学习与核算机视觉教程(13) | 方针检测 (SSD, YOLO系列) @CS231n
- 深度学习与核算机视觉教程(14) | 图画切割 (FCN, SegNet, U-Net, PSPNet, DeepLab, RefineNet) @CS231n
- 深度学习与核算机视觉教程(15) | 视觉模型可视化与可解释性 @CS231n
- 深度学习与核算机视觉教程(16) | 生成模型 (PixelRNN, PixelCNN, VAE, GAN) @CS231n
- 深度学习与核算机视觉教程(17) | 深度强化学习 (马尔可夫决策进程, Q-Learning, DQN) @CS231n
- 深度学习与核算机视觉教程(18) | 深度强化学习 (梯度战略, Actor-Critic, DDPG, A3C) @CS231n
ShowMeAI 系列教程引荐
- 大厂技能完成:引荐与广告核算处理方案
- 大厂技能完成:核算机视觉处理方案
- 大厂技能完成:自然言语处理职业处理方案
- 图解Python编程:从入门到通晓系列教程
- 图解数据剖析:从入门到通晓系列教程
- 图解AI数学根底:从入门到通晓系列教程
- 图解大数据技能:从入门到通晓系列教程
- 图解机器学习算法:从入门到通晓系列教程
- 机器学习实战:手把手教你玩转机器学习系列
- 深度学习教程:吴恩达专项课程 全套笔记解读
- 自然言语处理教程:斯坦福CS224n课程 课程带学与全套笔记解读
- 深度学习与核算机视觉教程:斯坦福CS231n 全套笔记解读