IntroKerasTF

Tensorflow 和 Keras 简介#

本笔记本是 AI for Beginners Curricula 的一部分。访问该仓库以获取完整的学习资料。

神经网络框架#

我们已经了解到,要训练神经网络,你需要:

  • 快速进行矩阵(张量)运算
  • 计算梯度以执行梯度下降优化

神经网络框架可以让你:

  • 在可用的计算设备上操作张量,无论是 CPU、GPU,甚至是 TPU
  • 自动计算梯度(所有内置张量函数都明确编程支持梯度计算)

可选功能:

  • 神经网络构造器 / 高级 API(将网络描述为一系列层)
  • 简单的训练函数(如 Scikit Learn 中的 fit
  • 除梯度下降之外的多种优化算法
  • 数据处理抽象(理想情况下也能在 GPU 上运行)

最受欢迎的框架#

  • Tensorflow 1.x - 第一个广泛使用的框架(由 Google 开发)。允许定义静态计算图,将其推送到 GPU,并显式地进行评估
  • PyTorch - 来自 Facebook 的框架,正在迅速流行起来
  • Keras - 基于 Tensorflow/PyTorch 的高级 API,用于统一和简化神经网络的使用(由 Francois Chollet 开发)
  • Tensorflow 2.x + Keras - Tensorflow 的新版本,集成了 Keras 功能,支持动态计算图,可以执行非常类似于 numpy(以及 PyTorch)的张量操作

我们将重点学习 Tensorflow 2.x 和 Keras。请确保你已安装 Tensorflow 2.x.x 版本:

pip install tensorflow

或者

conda install tensorflow
In [1]:
import tensorflow as tf
import numpy as np
print(tf.__version__)
2.7.0

基本概念:张量#

张量是一种多维数组。使用张量来表示不同类型的数据非常方便:

  • 400x400 - 黑白图片
  • 400x400x3 - 彩色图片
  • 16x400x400x3 - 包含16张彩色图片的小批量
  • 25x400x400x3 - 一秒钟的25帧视频
  • 8x25x400x400x3 - 包含8个1秒视频的小批量

简单张量#

你可以轻松地通过 np-array 列表创建简单的张量,或者生成随机张量:

In [2]:
a = tf.constant([[1,2],[3,4]])
print(a)
a = tf.random.normal(shape=(10,3))
print(a)
tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32)
tf.Tensor(
[[-0.33552304 -1.8252622  -1.8532339 ]
 [ 1.0871267  -1.2779568   0.5240014 ]
 [-0.12793781 -1.8618349  -0.9020286 ]
 [ 0.5948797   0.11144501 -2.0396452 ]
 [ 0.47620854  1.1726047  -0.4405675 ]
 [-0.27211484 -0.08985762 -0.03376012]
 [ 0.64274263  0.53368104 -0.9006528 ]
 [-0.43745974 -1.0081122  -0.13442488]
 [ 0.36497566  1.3221073  -1.8739727 ]
 [ 0.94821155 -0.02817811  1.3563292 ]], shape=(10, 3), dtype=float32)

您可以对张量进行算术运算,这些运算是逐元素执行的,就像在 numpy 中一样。如果需要,张量会自动扩展到所需的维度。要从张量中提取 numpy 数组,请使用 .numpy()

In [3]:
print(a-a[0])
print(tf.exp(a)[0].numpy())
tf.Tensor(
[[ 0.          0.          0.        ]
 [ 1.4226497   0.54730535  2.3772354 ]
 [ 0.20758523 -0.03657269  0.9512053 ]
 [ 0.93040276  1.9367073  -0.18641126]
 [ 0.8117316   2.9978669   1.4126664 ]
 [ 0.0634082   1.7354046   1.8194739 ]
 [ 0.97826564  2.3589432   0.9525811 ]
 [-0.1019367   0.81715     1.718809  ]
 [ 0.7004987   3.1473694  -0.02073872]
 [ 1.2837346   1.7970841   3.2095633 ]], shape=(10, 3), dtype=float32)
[0.71496403 0.16117539 0.15672949]

变量#

变量用于表示可以通过 assignassign_add 修改的张量值。它们通常用于表示神经网络的权重。

例如,这里有一个简单的方法来计算张量 a 所有行的总和:

In [4]:
s = tf.Variable(tf.zeros_like(a[0]))
for i in a:
  s.assign_add(i)

print(s)
<tf.Variable 'Variable:0' shape=(3,) dtype=float32, numpy=array([ 2.9411097, -2.9513645, -6.2979555], dtype=float32)>
In [5]:
tf.reduce_sum(a,axis=0)
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([ 2.9411097, -2.9513645, -6.2979555], dtype=float32)>

计算梯度#

为了进行反向传播,你需要计算梯度。这可以通过使用 tf.GradientTape() 来实现:

  • 在计算过程中添加 with tf.GradientTape
  • 使用 tape.watch 标记需要计算梯度的张量(所有变量会自动被监视)
  • 进行所需的计算(构建计算图)
  • 使用 tape.gradient 获取梯度
In [6]:
a = tf.random.normal(shape=(2, 2))
b = tf.random.normal(shape=(2, 2))

with tf.GradientTape() as tape:
  tape.watch(a)  # Start recording the history of operations applied to `a`
  c = tf.sqrt(tf.square(a) + tf.square(b))  # Do some math using `a`
  # What's the gradient of `c` with respect to `a`?
  dc_da = tape.gradient(c, a)
  print(dc_da)
tf.Tensor(
[[ 0.40935674 -0.3495818 ]
 [ 0.94165146 -0.33209163]], shape=(2, 2), dtype=float32)

示例 1:线性回归#

现在我们已经掌握了足够的知识来解决经典的线性回归问题。让我们生成一个小型的合成数据集:

In [7]:
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification, make_regression
from sklearn.model_selection import train_test_split
import random
In [8]:
np.random.seed(13) # pick the seed for reproducability - change it to explore the effects of random variations

train_x = np.linspace(0, 3, 120)
train_labels = 2 * train_x + 0.9 + np.random.randn(*train_x.shape) * 0.5

plt.scatter(train_x,train_labels)
<matplotlib.collections.PathCollection at 0x12892776880>
Notebook 输出图像

线性回归由一条直线 $f_{W,b}(x) = Wx+b$ 定义,其中 $W, b$ 是我们需要找到的模型参数。在数据集 ${x_i,y_i}{i=1}^N$ 上的误差(也称为损失函数)可以定义为均方误差: $$ \mathcal{L}(W,b) = {1\over N}\sum{i=1}^N (f_{W,b}(x_i)-y_i)^2 $$

让我们定义我们的模型和损失函数:

In [9]:
input_dim = 1
output_dim = 1
learning_rate = 0.1

# This is our weight matrix
w = tf.Variable([[100.0]])
# This is our bias vector
b = tf.Variable(tf.zeros(shape=(output_dim,)))

def f(x):
  return tf.matmul(x,w) + b

def compute_loss(labels, predictions):
  return tf.reduce_mean(tf.square(labels - predictions))

我们将通过一系列小批量数据来训练模型。我们将使用梯度下降法,根据以下公式调整模型参数:
$$ \begin{array}{l} W^{(n+1)}=W^{(n)}-\eta\frac{\partial\mathcal{L}}{\partial W} \ b^{(n+1)}=b^{(n)}-\eta\frac{\partial\mathcal{L}}{\partial b} \ \end{array} $$

In [10]:
def train_on_batch(x, y):
  with tf.GradientTape() as tape:
    predictions = f(x)
    loss = compute_loss(y, predictions)
    # Note that `tape.gradient` works with a list as well (w, b).
    dloss_dw, dloss_db = tape.gradient(loss, [w, b])
  w.assign_sub(learning_rate * dloss_dw)
  b.assign_sub(learning_rate * dloss_db)
  return loss

让我们进行训练。我们将多次遍历数据集(所谓的epochs),将其分成小批量并调用上面定义的函数:

In [11]:
# Shuffle the data.
indices = np.random.permutation(len(train_x))
features = tf.constant(train_x[indices],dtype=tf.float32)
labels = tf.constant(train_labels[indices],dtype=tf.float32)
In [12]:
batch_size = 4
for epoch in range(10):
  for i in range(0,len(features),batch_size):
    loss = train_on_batch(tf.reshape(features[i:i+batch_size],(-1,1)),tf.reshape(labels[i:i+batch_size],(-1,1)))
  print('Epoch %d: last batch loss = %.4f' % (epoch, float(loss)))
Epoch 0: last batch loss = 94.5247
Epoch 1: last batch loss = 9.3428
Epoch 2: last batch loss = 1.4166
Epoch 3: last batch loss = 0.5224
Epoch 4: last batch loss = 0.3807
Epoch 5: last batch loss = 0.3495
Epoch 6: last batch loss = 0.3413
Epoch 7: last batch loss = 0.3390
Epoch 8: last batch loss = 0.3384
Epoch 9: last batch loss = 0.3382

我们现在已经获得了优化后的参数 $W$ 和 $b$。注意,它们的值与生成数据集时使用的原始值相似($W=2, b=1$)。

In [13]:
w,b
(<tf.Variable 'Variable:0' shape=(1, 1) dtype=float32, numpy=array([[1.8616779]], dtype=float32)>,
 <tf.Variable 'Variable:0' shape=(1,) dtype=float32, numpy=array([1.0710956], dtype=float32)>)
In [14]:
plt.scatter(train_x,train_labels)
x = np.array([min(train_x),max(train_x)])
y = w.numpy()[0,0]*x+b.numpy()[0]
plt.plot(x,y,color='red')
[<matplotlib.lines.Line2D at 0x12892ae5eb0>]
Notebook 输出图像

计算图与GPU计算#

每当我们计算张量表达式时,Tensorflow会构建一个可以在可用计算设备(例如CPU或GPU)上运行的计算图。由于我们在代码中使用了任意的Python函数,这些函数无法作为计算图的一部分,因此当我们在GPU上运行代码时,需要在CPU和GPU之间来回传递数据,并在CPU上计算自定义函数。

Tensorflow允许我们使用@tf.function装饰器标记Python函数,这样可以将该函数作为同一计算图的一部分。这个装饰器可以应用于使用标准Tensorflow张量操作的函数。

In [15]:
@tf.function
def train_on_batch(x, y):
  with tf.GradientTape() as tape:
    predictions = f(x)
    loss = compute_loss(y, predictions)
    # Note that `tape.gradient` works with a list as well (w, b).
    dloss_dw, dloss_db = tape.gradient(loss, [w, b])
  w.assign_sub(learning_rate * dloss_dw)
  b.assign_sub(learning_rate * dloss_db)
  return loss

代码没有变化,但如果您在GPU上运行此代码并使用更大的数据集,您会注意到速度上的差异。

数据集 API#

Tensorflow 提供了一个方便的 API 来处理数据。让我们尝试使用它。同时,我们也将从零开始训练我们的模型。

In [16]:
w.assign([[10.0]])
b.assign([0.0])

# Create a tf.data.Dataset object for easy batched iteration
dataset = tf.data.Dataset.from_tensor_slices((train_x.astype(np.float32), train_labels.astype(np.float32)))
dataset = dataset.shuffle(buffer_size=1024).batch(256)

for epoch in range(10):
  for step, (x, y) in enumerate(dataset):
    loss = train_on_batch(tf.reshape(x,(-1,1)), tf.reshape(y,(-1,1)))
  print('Epoch %d: last batch loss = %.4f' % (epoch, float(loss)))
Epoch 0: last batch loss = 173.4585
Epoch 1: last batch loss = 13.8459
Epoch 2: last batch loss = 4.5407
Epoch 3: last batch loss = 3.7364
Epoch 4: last batch loss = 3.4334
Epoch 5: last batch loss = 3.1790
Epoch 6: last batch loss = 2.9458
Epoch 7: last batch loss = 2.7311
Epoch 8: last batch loss = 2.5332
Epoch 9: last batch loss = 2.3508

示例 2:分类#

现在我们来考虑一个二分类问题。一个很好的例子是根据肿瘤的大小和年龄来区分恶性和良性。

核心模型与回归类似,但我们需要使用不同的损失函数。让我们从生成样本数据开始:

In [40]:
np.random.seed(0) # pick the seed for reproducibility - change it to explore the effects of random variations

n = 100
X, Y = make_classification(n_samples = n, n_features=2,
                           n_redundant=0, n_informative=2, flip_y=0.05,class_sep=1.5)
X = X.astype(np.float32)
Y = Y.astype(np.int32)

split = [ 70*n//100, (15+70)*n//100 ]
train_x, valid_x, test_x = np.split(X, split)
train_labels, valid_labels, test_labels = np.split(Y, split)
In [41]:
def plot_dataset(features, labels, W=None, b=None):
    # prepare the plot
    fig, ax = plt.subplots(1, 1)
    ax.set_xlabel('$x_i[0]$ -- (feature 1)')
    ax.set_ylabel('$x_i[1]$ -- (feature 2)')
    colors = ['r' if l else 'b' for l in labels]
    ax.scatter(features[:, 0], features[:, 1], marker='o', c=colors, s=100, alpha = 0.5)
    if W is not None:
        min_x = min(features[:,0])
        max_x = max(features[:,1])
        min_y = min(features[:,1])*(1-.1)
        max_y = max(features[:,1])*(1+.1)
        cx = np.array([min_x,max_x],dtype=np.float32)
        cy = (0.5-W[0]*cx-b)/W[1]
        ax.plot(cx,cy,'g')
        ax.set_ylim(min_y,max_y)
    fig.show()
In [42]:
plot_dataset(train_x, train_labels)
C:\Users\dmitryso\AppData\Local\Temp/ipykernel_66184/2721537645.py:17: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
  fig.show()
Notebook 输出图像

数据归一化#

在训练之前,通常会将输入特征调整到标准范围 [0,1](或 [-1,1])。具体原因我们会在课程后面详细讨论,但简单来说,原因如下:我们希望通过网络传递的值不会变得过大或过小,通常会约定将所有值保持在接近 0 的小范围内。因此,我们用小的随机数初始化权重,并将信号保持在相同的范围内。

在进行数据归一化时,需要减去最小值并除以范围。我们使用训练数据计算最小值和范围,然后用训练集的最小值和范围对测试/验证数据集进行归一化。这是因为在实际应用中,我们通常只知道训练集,而无法预知网络将要预测的所有新输入值。偶尔会有新值超出 [0,1] 范围,但这并不重要。

In [43]:
train_x_norm = (train_x-np.min(train_x)) / (np.max(train_x)-np.min(train_x))
valid_x_norm = (valid_x-np.min(train_x)) / (np.max(train_x)-np.min(train_x))
test_x_norm = (test_x-np.min(train_x)) / (np.max(train_x)-np.min(train_x))

训练单层感知机#

让我们使用 Tensorflow 的梯度计算机制来训练单层感知机。

我们的神经网络将有两个输入和一个输出。权重矩阵 $W$ 的大小为 $2\times1$,偏置向量 $b$ 的大小为 $1$。

核心模型与之前的例子相同,但损失函数将使用逻辑损失。为了应用逻辑损失,我们需要将网络输出的值转换为概率,即需要通过 sigmoid 激活函数将输出 $z$ 映射到 [0,1] 范围:$p=\sigma(z)$。

如果我们得到第 i 个输入值对应的实际类别 $y_i\in{0,1}$ 的概率 $p_i$,我们可以计算损失为 $\mathcal{L_i}=-(y_i\log p_i + (1-y_i)\log(1-p_i))$。

在 Tensorflow 中,这两个步骤(应用 sigmoid 和逻辑损失)可以通过调用 sigmoid_cross_entropy_with_logits 函数一次性完成。由于我们在小批量中训练网络,因此需要使用 reduce_mean 对小批量中的所有元素的损失进行平均:

In [52]:
W = tf.Variable(tf.random.normal(shape=(2,1)),dtype=tf.float32)
b = tf.Variable(tf.zeros(shape=(1,),dtype=tf.float32))

learning_rate = 0.1

@tf.function
def train_on_batch(x, y):
  with tf.GradientTape() as tape:
    z = tf.matmul(x, W) + b
    loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y,logits=z))
  dloss_dw, dloss_db = tape.gradient(loss, [W, b])
  W.assign_sub(learning_rate * dloss_dw)
  b.assign_sub(learning_rate * dloss_db)
  return loss

我们将使用包含16个元素的小批量,并进行几轮训练:

In [59]:
# Create a tf.data.Dataset object for easy batched iteration
dataset = tf.data.Dataset.from_tensor_slices((train_x_norm.astype(np.float32), train_labels.astype(np.float32)))
dataset = dataset.shuffle(128).batch(2)

for epoch in range(10):
  for step, (x, y) in enumerate(dataset):
    loss = train_on_batch(x, tf.expand_dims(y,1))
  print('Epoch %d: last batch loss = %.4f' % (epoch, float(loss)))
Epoch 0: last batch loss = 0.3823
Epoch 1: last batch loss = 0.5243
Epoch 2: last batch loss = 0.4510
Epoch 3: last batch loss = 0.3261
Epoch 4: last batch loss = 0.4177
Epoch 5: last batch loss = 0.3323
Epoch 6: last batch loss = 0.6294
Epoch 7: last batch loss = 0.6334
Epoch 8: last batch loss = 0.2571
Epoch 9: last batch loss = 0.3425

为了确保我们的训练有效,让我们绘制出分隔两类的直线。分隔线由方程 $W\times x + b = 0.5$ 定义。

In [60]:
plot_dataset(train_x,train_labels,W.numpy(),b.numpy())
C:\Users\dmitryso\AppData\Local\Temp/ipykernel_66184/2721537645.py:17: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
  fig.show()
Notebook 输出图像

让我们看看我们的模型在验证数据上的表现。

In [61]:
pred = tf.matmul(test_x,W)+b
fig,ax = plt.subplots(1,2)
ax[0].scatter(test_x[:,0],test_x[:,1],c=pred[:,0]>0.5)
ax[1].scatter(test_x[:,0],test_x[:,1],c=valid_labels)
<matplotlib.collections.PathCollection at 0x12892a01460>
Notebook 输出图像

要计算验证数据的准确性,我们可以将布尔类型转换为浮点数,然后计算平均值:

In [62]:
tf.reduce_mean(tf.cast(((pred[0]>0.5)==test_labels),tf.float32))
<tf.Tensor: shape=(), dtype=float32, numpy=0.46666667>

让我们来解释这里发生了什么:

  • pred 是网络预测的值。它们并不完全是概率,因为我们没有使用激活函数,但大于 0.5 的值对应于类别 1,而小于 0.5 的值对应于类别 0。
  • pred[0]>0.5 会创建一个布尔张量,其中 True 对应于类别 1,False 对应于类别 0。
  • 我们将该张量与期望的标签 valid_labels 进行比较,得到一个布尔向量,表示预测是否正确,其中 True 表示预测正确,False 表示预测错误。
  • 我们使用 tf.cast 将该张量转换为浮点数。
  • 然后我们使用 tf.reduce_mean 计算平均值——这正是我们想要的准确率。

使用 TensorFlow/Keras 优化器#

TensorFlow 与 Keras 紧密集成,提供了许多实用功能。例如,我们可以使用不同的优化算法。让我们来试试,同时在训练过程中打印获得的准确率。

In [63]:
optimizer = tf.keras.optimizers.Adam(0.01)

W = tf.Variable(tf.random.normal(shape=(2,1)))
b = tf.Variable(tf.zeros(shape=(1,),dtype=tf.float32))

@tf.function
def train_on_batch(x, y):
  vars = [W, b]
  with tf.GradientTape() as tape:
    z = tf.sigmoid(tf.matmul(x, W) + b)
    loss = tf.reduce_mean(tf.keras.losses.binary_crossentropy(z,y))
    correct_prediction = tf.equal(tf.round(y), tf.round(z))
    acc = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    grads = tape.gradient(loss, vars)
    optimizer.apply_gradients(zip(grads,vars))
  return loss,acc

for epoch in range(20):
  for step, (x, y) in enumerate(dataset):
    loss,acc = train_on_batch(tf.reshape(x,(-1,2)), tf.reshape(y,(-1,1)))
  print('Epoch %d: last batch loss = %.4f, acc = %.4f' % (epoch, float(loss),acc))
Epoch 0: last batch loss = 4.7787, acc = 1.0000
Epoch 1: last batch loss = 8.4343, acc = 0.5000
Epoch 2: last batch loss = 8.3255, acc = 0.5000
Epoch 3: last batch loss = 7.5579, acc = 0.5000
Epoch 4: last batch loss = 6.5254, acc = 0.5000
Epoch 5: last batch loss = 7.3800, acc = 0.5000
Epoch 6: last batch loss = 7.7586, acc = 0.5000
Epoch 7: last batch loss = 10.4724, acc = 0.0000
Epoch 8: last batch loss = 9.4423, acc = 0.5000
Epoch 9: last batch loss = 4.1888, acc = 1.0000
Epoch 10: last batch loss = 11.2127, acc = 0.0000
Epoch 11: last batch loss = 9.0417, acc = 0.5000
Epoch 12: last batch loss = 7.9847, acc = 0.5000
Epoch 13: last batch loss = 3.7879, acc = 1.0000
Epoch 14: last batch loss = 6.8455, acc = 0.5000
Epoch 15: last batch loss = 6.5204, acc = 0.5000
Epoch 16: last batch loss = 9.2386, acc = 0.5000
Epoch 17: last batch loss = 6.2447, acc = 0.5000
Epoch 18: last batch loss = 3.9107, acc = 1.0000
Epoch 19: last batch loss = 5.7645, acc = 1.0000

任务 1:绘制训练过程中训练数据和验证数据的损失函数和准确率的图表

任务 2:尝试使用此代码解决 MNIST 分类问题。提示:使用 softmax_crossentropy_with_logitssparse_softmax_cross_entropy_with_logits 作为损失函数。在第一种情况下,你需要以独热编码的形式提供期望的输出值;在第二种情况下,则以整数类别编号的形式提供。

Keras#

人性化的深度学习#

  • Keras 是由 Francois Chollet 开发的一个库,最初用于在 Tensorflow、CNTK 和 Theano 之上运行,以统一所有底层框架。虽然你仍然可以单独安装 Keras,但不建议这样做。
  • 现在 Keras 已经包含在 Tensorflow 库中
  • 你可以轻松通过层构建神经网络
  • 包含 fit 函数来完成所有训练,还有许多函数可以处理常见数据(图片、文本等)
  • 提供大量示例
  • 支持功能性 API 和顺序 API

Keras 为神经网络提供了更高层次的抽象,使我们可以通过层、模型和优化器进行操作,而不是直接处理张量和梯度。

Keras 创作者编写的经典深度学习书籍:Deep Learning with Python

功能性 API#

使用功能性 API 时,我们将网络的输入定义为 keras.Input,然后通过一系列计算得到输出。最后,我们定义模型为一个将输入转换为输出的对象。

一旦获得了模型对象,我们需要:

  • 编译模型,通过指定损失函数和优化器来配置模型
  • 训练模型,通过调用 fit 函数并提供训练数据(以及可能的验证数据)
In [64]:
inputs = tf.keras.Input(shape=(2,))
z = tf.keras.layers.Dense(1,kernel_initializer='glorot_uniform',activation='sigmoid')(inputs)
model = tf.keras.models.Model(inputs,z)

model.compile(tf.keras.optimizers.Adam(0.1),'binary_crossentropy',['accuracy'])
model.summary()
h = model.fit(train_x_norm,train_labels,batch_size=8,epochs=15)
Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_1 (InputLayer)        [(None, 2)]               0         
                                                                 
 dense (Dense)               (None, 1)                 3         
                                                                 
=================================================================
Total params: 3
Trainable params: 3
Non-trainable params: 0
_________________________________________________________________
Epoch 1/15
9/9 [==============================] - 1s 2ms/step - loss: 0.7812 - accuracy: 0.2857
Epoch 2/15
9/9 [==============================] - 0s 2ms/step - loss: 0.7142 - accuracy: 0.4000
Epoch 3/15
9/9 [==============================] - 0s 2ms/step - loss: 0.6683 - accuracy: 0.6143
Epoch 4/15
9/9 [==============================] - 0s 2ms/step - loss: 0.6221 - accuracy: 0.8429
Epoch 5/15
9/9 [==============================] - 0s 2ms/step - loss: 0.5843 - accuracy: 0.8857
Epoch 6/15
9/9 [==============================] - 0s 2ms/step - loss: 0.5447 - accuracy: 0.9429
Epoch 7/15
9/9 [==============================] - 0s 2ms/step - loss: 0.5135 - accuracy: 0.9286
Epoch 8/15
9/9 [==============================] - 0s 2ms/step - loss: 0.4878 - accuracy: 0.9429
Epoch 9/15
9/9 [==============================] - 0s 2ms/step - loss: 0.4679 - accuracy: 0.9429
Epoch 10/15
9/9 [==============================] - 0s 2ms/step - loss: 0.4446 - accuracy: 0.9429
Epoch 11/15
9/9 [==============================] - 0s 2ms/step - loss: 0.4349 - accuracy: 0.8714
Epoch 12/15
9/9 [==============================] - 0s 2ms/step - loss: 0.4156 - accuracy: 0.9286
Epoch 13/15
9/9 [==============================] - 0s 2ms/step - loss: 0.4019 - accuracy: 0.9429
Epoch 14/15
9/9 [==============================] - 0s 2ms/step - loss: 0.3908 - accuracy: 0.9286
Epoch 15/15
9/9 [==============================] - 0s 2ms/step - loss: 0.3777 - accuracy: 0.9286
In [65]:
plt.plot(h.history['accuracy'])
[<matplotlib.lines.Line2D at 0x12894b95250>]
Notebook 输出图像

顺序式 API#

或者,我们可以将模型视为一个层的序列,只需通过将这些层添加到 model 对象中来指定它们:

In [66]:
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Dense(5,activation='sigmoid',input_shape=(2,)))
model.add(tf.keras.layers.Dense(1,activation='sigmoid'))

model.compile(tf.keras.optimizers.Adam(0.1),'binary_crossentropy',['accuracy'])
model.summary()
model.fit(train_x_norm,train_labels,validation_data=(test_x_norm,test_labels),batch_size=8,epochs=15)
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_1 (Dense)             (None, 5)                 15        
                                                                 
 dense_2 (Dense)             (None, 1)                 6         
                                                                 
=================================================================
Total params: 21
Trainable params: 21
Non-trainable params: 0
_________________________________________________________________
Epoch 1/15
9/9 [==============================] - 1s 64ms/step - loss: 0.6994 - accuracy: 0.5000 - val_loss: 0.6719 - val_accuracy: 0.4667
Epoch 2/15
9/9 [==============================] - 0s 6ms/step - loss: 0.6635 - accuracy: 0.5429 - val_loss: 0.6531 - val_accuracy: 0.4667
Epoch 3/15
9/9 [==============================] - 0s 5ms/step - loss: 0.6469 - accuracy: 0.5857 - val_loss: 0.5775 - val_accuracy: 1.0000
Epoch 4/15
9/9 [==============================] - 0s 4ms/step - loss: 0.5639 - accuracy: 0.9143 - val_loss: 0.5395 - val_accuracy: 0.7333
Epoch 5/15
9/9 [==============================] - 0s 5ms/step - loss: 0.5236 - accuracy: 0.7143 - val_loss: 0.4498 - val_accuracy: 0.9333
Epoch 6/15
9/9 [==============================] - 0s 5ms/step - loss: 0.4573 - accuracy: 0.8714 - val_loss: 0.3584 - val_accuracy: 1.0000
Epoch 7/15
9/9 [==============================] - 0s 5ms/step - loss: 0.3867 - accuracy: 0.8714 - val_loss: 0.2989 - val_accuracy: 0.9333
Epoch 8/15
9/9 [==============================] - 0s 7ms/step - loss: 0.3388 - accuracy: 0.8857 - val_loss: 0.2204 - val_accuracy: 1.0000
Epoch 9/15
9/9 [==============================] - 0s 6ms/step - loss: 0.2815 - accuracy: 0.9429 - val_loss: 0.1957 - val_accuracy: 1.0000
Epoch 10/15
9/9 [==============================] - 0s 6ms/step - loss: 0.2692 - accuracy: 0.8857 - val_loss: 0.1323 - val_accuracy: 1.0000
Epoch 11/15
9/9 [==============================] - 0s 5ms/step - loss: 0.2591 - accuracy: 0.9429 - val_loss: 0.1105 - val_accuracy: 1.0000
Epoch 12/15
9/9 [==============================] - 0s 6ms/step - loss: 0.2229 - accuracy: 0.9286 - val_loss: 0.1051 - val_accuracy: 1.0000
Epoch 13/15
9/9 [==============================] - 0s 5ms/step - loss: 0.2146 - accuracy: 0.9143 - val_loss: 0.0919 - val_accuracy: 1.0000
Epoch 14/15
9/9 [==============================] - 0s 5ms/step - loss: 0.2031 - accuracy: 0.9429 - val_loss: 0.0859 - val_accuracy: 1.0000
Epoch 15/15
9/9 [==============================] - 0s 5ms/step - loss: 0.1997 - accuracy: 0.9429 - val_loss: 0.0829 - val_accuracy: 1.0000
<keras.callbacks.History at 0x12894cfba30>

分类损失函数#

正确指定网络最后一层的损失函数和激活函数非常重要。主要规则如下:

  • 如果网络有一个输出(二分类),我们使用 sigmoid 激活函数;对于 多分类,使用 softmax
  • 如果输出类别以独热编码表示,损失函数应为 交叉熵损失(分类交叉熵);如果输出包含类别编号,则使用 稀疏分类交叉熵。对于 二分类,使用 二元交叉熵(与 对数损失 相同)。
  • 多标签分类 是指一个对象可以同时属于多个类别。在这种情况下,我们需要使用独热编码对标签进行编码,并使用 sigmoid 作为激活函数,以确保每个类别的概率在 0 和 1 之间。
分类类型 标签格式 激活函数 损失函数
二分类 第一个类别的概率 sigmoid binary crossentropy
二分类 独热编码(2 个输出) softmax categorical crossentropy
多分类 独热编码 softmax categorical crossentropy
多分类 类别编号 softmax sparse categorical crossentropy
多标签 独热编码 sigmoid categorical crossentropy

二分类也可以作为两输出的多分类的特殊情况处理。在这种情况下,我们需要使用 softmax

任务 3
使用 Keras 训练 MNIST 分类器:

  • 注意,Keras 包含一些标准数据集,包括 MNIST。要使用 Keras 中的 MNIST,只需要几行代码即可(更多信息请参考 这里)。
  • 尝试多种网络配置,包括不同数量的层/神经元、激活函数。

你能够达到的最高准确率是多少?

要点#

  • Tensorflow 允许你在低层次上操作张量,提供了最大的灵活性。
  • 有一些方便的工具可以用来处理数据(td.Data)和层(tf.layers)。
  • 对于初学者或常见任务,推荐使用 Keras,它可以通过层来构建网络。
  • 如果需要非标准的架构,你可以实现自己的 Keras 层,然后在 Keras 模型中使用它。
  • 也可以考虑了解一下 PyTorch,并对比两者的实现方式。

Keras 的创建者提供了一个关于 Keras 和 Tensorflow 2.0 的优秀示例笔记本,可以在这里找到。


免责声明
本文档使用AI翻译服务 Co-op Translator 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。我们对因使用此翻译而引起的任何误解或误读不承担责任。