python随缘学习笔记

神经网络的数学基础(略)

初识神经网络

神经网络的数据表示

数据存储在多维Numpy数组中 => 张量(tensor)

当前所有机器学习系统都使用张量作为基本数据结构

张量是一个数据容器

矩阵是二维张量

张量是矩阵向任意维度的推广

张量的维度通常叫做轴(axis)

在Numpy中可以用ndim属性查看张量中轴的个数

标量(0D张量)

大概就是数组中只有一个数字

array([12, 3, 6, 14, 7])

向量(1D张量)

vector

一维数组

array([12, 3, 6, 14, 7])

矩阵(2D张量)

array([
    	[5, 78, 2, 34, 0],
		[6, 79, 3, 35, 1],
		[7, 80, 4, 36, 2]
	])

3D张量与更高维张量

将多个矩阵组合成一个新的数组,可以得到一个 3D 张量

将多个 3D 张量组合成一个数组,可以创建一个 4D 张量

深度学习处理的一般是 0D 到 4D 的张量,但处理视频数据时可能会遇到 5D 张量。

张量的关键属性

轴的个数(阶)

3D 张量有 3 个轴,矩阵有 2 个轴。这在 Numpy 等 Python 库中也叫张量的 ndim。

形状

一个整数元组,表示张量沿每个轴的维度大小(元素个数)

前面矩阵示例的形状为 (3, 5)

3D 张量示例的形状为 (3, 3, 5)

向量的形状只包含一个元素,比如 (5,)

而标量的形状为空,即 ()

数据类型

张量中所包含数据的类型

在 Python 库中通常叫作 dtype

张量的类型可以是 float32、uint8、float64 等

在极少数情况下,你可能会遇到字符(char)张量

Numpy(以及大多数其他库)中不存在字符串张量

因为张量存储在预先分配的连续内存段中,而字符串的长度是可变的,无法用这种方式存储。

在 Numpy 中操作张量

数据批量的概念

通常来说,深度学习中所有数据张量的第一个轴(0 轴,因为索引从0开始)都是样本轴(samples axis,有时也叫样本维度)

深度学习模型不会同时处理整个数据集,而是将数据拆分成小批量

对于这种批量张量,第一个轴(0 轴)叫作批量轴(batch axis)或批量维度(batch dimension)

现实世界中的数据张量

  • 向量数据:2D 张量,形状为 (samples, features)

  • 时间序列数据或序列数据:3D 张量,形状为 (samples, timesteps, features)

  • 图像:4D 张量,形状为 (samples, height, width, channels) 或 (samples, channels, height, width)

  • 视频:5D 张量,形状为 (samples, frames, height, width, channels) 或 (samples, frames, channels, height, width)

张量运算

神经网络入门

神经网络剖析

训练神经网络主要围绕一下四个方面

  • 层,多个层组合成网络模型
  • 输入数据和相应目标
  • 损失函数,用于学习的反馈信号
  • 优化器,决定学习过程如何进行

image-20211224190318449

多个层链接在一起组成了网络,将输入数据映射为预测值。

损失函数将这些预测值与目标进行比较,得到损失值,用于衡量网络预测值与预期结果的匹配程度。

优化器使用这个损失值来更新网络的权重

深度学习的基础组件

神经网络的基本数据结构是层

层是一个数据处理模块,将一个或多个输入张量转换为一个或多个输出张量

层兼容性(layer compatibility):每一层只接受特定形状的输入张量,并返回特定形状的输出张量

不同的张量格式与不同的数据处理类型需要用到不同的层

(samples, features) 2D 张量

密集连接层

[densely connected layer,也叫全连接层(fully connected layer)或密集层(dense layer)对应于 KerasDense 类

(samples, timesteps, features) 的 3D 张量

循环层(recurrent layer,比如 Keras 的 LSTM 层

图像数据 4D 张量

二维卷积层(Keras 的 Conv2D

模型

层构成的网络

深度学习模型是层构成的有向无环图

损失函数与优化器

一旦确定了网络架构,你还需要选择以下两个参数

损失函数

在训练过程中需要将其最小化

衡量当前任务是否已成功完成

优化器

决定如何基于损失函数对网络进行更新

它执行的是随机梯度下降(SGD)的某个变体

原则

对于二分类问题,你可以使用二元交叉熵(binary crossentropy)损失函数

对于多分类问题,可以用分类交叉熵(categorical crossentropy)损失函数

对于回归问题,可以用均方误差(mean-squared error)损失函数

对于序列学习问题,可以用联结主义时序分类(CTC,connectionist temporal classification)损失函数

Keras

流程

(1) 定义训练数据:输入张量和目标张量。

(2) 定义层组成的网络(或模型),将输入映射到目标。

使用 Sequential 类(仅用于层的线性堆叠,这是目前最常见的网络架构)

另一种是函数式 API(functional API,用于层组成的有向无环图,让你可以构建任意形式的架构)

(3) 配置学习过程:选择损失函数、优化器和需要监控的指标。

单一损失函数的例子,目前最常见的

from keras import optimizers

model.compile(optimizer=optimizers.RMSprop(lr=0.001),loss='mse', metrics=['accuracy'])

(4) 调用模型的 fit 方法在训练数据上进行迭代。

model.fit(input_tensor, target_tensor, batch_size=128, epochs=10)

建立深度学习工作站

推荐

Ubuntu

Jupyter

NVIDIA GPU

电影评论分类:二分类问题

# 加载数据集
from keras.datasets import imdb

# num_words=10000 的意思是仅保留训练数据中前 10 000 个最常出现的单词
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
out:
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz
17465344/17464789 [==============================] - 3s 0us/step
17473536/17464789 [==============================] - 3s 0us/step

要将列表转换为张量

转换方法有以下两种
‰ 填充列表,使其具有相同的长度,再将列表转换成形状为 (samples, word_indices)的整数张量,然后网络第一层使用能处理这种整数张量的层(即 Embedding 层)
‰ 对列表进行 one-hot 编码,将其转换为 0 和 1 组成的向量。举个例子,序列 [3, 5] 将会被转换为 10 000 维向量,只有索引为 3 和 5 的元素是 1,其余元素都是 0。然后网络第一层可以用 Dense 层,它能够处理浮点数向量数据。

下面我们采用后一种方法将数据向量化。为了加深理解,你可以手动实现这一方法

import numpy as np
# 创建一个形状为(len(sequences),dimension) 的零矩阵
def vectorize_sequences(sequences, dimension=10000):
    results = np.zeros((len(sequences), dimension)) 
    for i, sequence in enumerate(sequences):
        # 将 results[i] 的指定索引设为 1
        results[i, sequence] = 1. 
    return results
# 向量化
x_train = vectorize_sequences(train_data) 
x_test = vectorize_sequences(test_data)

#将标签向量化
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
x_train[0]
out:
array([0., 1., 1., ..., 0., 0., 0.])
y_train[0]
out:
1.0

构建网络

  • 输入数据是向量
  • 标签是标量(1 和 0)
    最简单的情况
    带有 relu 激活的全连接层(Dense)的简单堆叠,在这种问题上表现很好

如Dense(16, activation='relu')

参数(16)是该层隐藏单元的个数

一个隐藏单元(hidden unit)是该层表示空间的一个维度。我们在第 2 章讲过,每个带有 relu 激活的 Dense 层都实现了下列张量运算:
output = relu(dot(W, input) + b)
16 个隐藏单元对应的权重矩阵 W 的形状为 (input_dimension, 16),与 W 做点积相当于将输入数据投影到 16 维表示空间中(然后再加上偏置向量 b 并应用 relu 运算)。你可以将表示空间的维度直观地理解为“网络学习内部表示时所拥有的自由度”。
隐藏单元越多(即更高维的表示空间),网络越能够学到更加复杂的表示
但网络的计算代价也变得更大,而且可能会导致学到不好的模式(这种模式会提高训练数据上的性能,但不会提高测试数据上的性能)

对于这种 Dense 层的堆叠,你需要确定以下两个关键架构:
‰ 网络有多少层;
‰ 每层有多少个隐藏单元。

第 4 章中的原则将会指导你对上述问题做出选择。现在你只需要相信我选择的下列架构:
‰ 两个中间层,每层都有 16 个隐藏单元;
‰ 第三层输出一个标量,预测当前评论的情感

中间层使用 relu 作为激活函数,最后一层使用 sigmoid 激活以输出一个 0~1 范围内的概率值(表示样本的目标值等于 1 的可能性,即评论为正面的可能性)

relu(rectified linear unit,整流线性单元)函数将所有负值归零,而 sigmoid 函数则将任意值“压缩”到 [0, 1] 区间内(见图 3-5),其输出值可以看作概率值。

模型定义

from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

什么是激活函数?为什么要使用激活函数?
如果没有 relu 等激活函数(也叫非线性),Dense 层将只包含两个线性运算
点积和加法:output = dot(W, input) + b
这样 Dense 层就只能学习输入数据的线性变换(仿射变换)
该层的假设空间是从输入数据到 16 位空间所有可能的线性变换集合。
这种假设空间非常有限,无法利用多个表示层的优势,因为多个线性层堆叠实现的仍是线性运算,添加层数并不会扩展假设空间。
为了得到更丰富的假设空间,从而充分利用多层表示的优势,你需要添加非线性或激活函数。
relu 是深度学习中最常用的激活函数,但还有许多其他函数可选,它们都有类似的奇怪名称,比如 prelu、elu 等

选择损失函数和优化器

面对的是一个二分类问题
网络输出是一个概率值(网络最后一层使用 sigmoid 激活函数,仅包含一个单元)
最好使用 binary_crossentropy(二元交叉熵)损失
还可以使用 mean_squared_error(均方误差)
但对于输出概率值的模型,交叉熵(crossentropy)往往是最好的选择
交叉熵是来自于信息论领域的概念,用于衡量概率分布之间的距离,在这个例子中就是真实分布与预测值之间的距离。

用 rmsprop 优化器和 binary_crossentropy 损失函数来配置模型

model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['accuracy'])

有时你可能希望配置自定义优化器的参数
或者传入自定义的损失函数或指标函数
前者可通过向 optimizer 参数传入一个优化器类实例来实现

from keras import optimizers
model.compile(optimizer=optimizers.RMSprop(lr=0.001),loss='binary_crossentropy',metrics=['accuracy'])

后者可通过向 loss 和 metrics 参数传入函数对象来实现

from keras import losses
from keras import metrics

model.compile(optimizer=optimizers.RMSprop(lr=0.001),loss=losses.binary_crossentropy,metrics=[metrics.binary_accuracy])
# 留出验证集
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]

#训练模型
history = model.fit(partial_x_train,
                    partial_y_train,
                    # 对 x_train 和 y_train 两个张量中的所有样本进行 20 次迭代
                    epochs=20,
                    # 使用 512 个样本组成的小批量
                    batch_size=512,
                    validation_data=(x_val, y_val))
out:
Epoch 1/20
30/30 [==============================] - 2s 46ms/step - loss: 0.5285 - accuracy: 0.7911 - val_loss: 0.4037 - val_accuracy: 0.8633
Epoch 2/20
30/30 [==============================] - 0s 14ms/step - loss: 0.3218 - accuracy: 0.9001 - val_loss: 0.3188 - val_accuracy: 0.8807
Epoch 3/20
30/30 [==============================] - 0s 13ms/step - loss: 0.2317 - accuracy: 0.9273 - val_loss: 0.2827 - val_accuracy: 0.8898
Epoch 4/20
30/30 [==============================] - 0s 13ms/step - loss: 0.1814 - accuracy: 0.9419 - val_loss: 0.2740 - val_accuracy: 0.8920
Epoch 5/20
30/30 [==============================] - 0s 13ms/step - loss: 0.1463 - accuracy: 0.9531 - val_loss: 0.2789 - val_accuracy: 0.8883
Epoch 6/20
30/30 [==============================] - 0s 13ms/step - loss: 0.1225 - accuracy: 0.9635 - val_loss: 0.2920 - val_accuracy: 0.8851
Epoch 7/20
30/30 [==============================] - 0s 13ms/step - loss: 0.1019 - accuracy: 0.9686 - val_loss: 0.3358 - val_accuracy: 0.8753
Epoch 8/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0857 - accuracy: 0.9748 - val_loss: 0.3241 - val_accuracy: 0.8823
Epoch 9/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0689 - accuracy: 0.9809 - val_loss: 0.3563 - val_accuracy: 0.8805
Epoch 10/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0565 - accuracy: 0.9863 - val_loss: 0.3883 - val_accuracy: 0.8788
Epoch 11/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0469 - accuracy: 0.9879 - val_loss: 0.4084 - val_accuracy: 0.8778
Epoch 12/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0378 - accuracy: 0.9917 - val_loss: 0.4345 - val_accuracy: 0.8717
Epoch 13/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0312 - accuracy: 0.9939 - val_loss: 0.4619 - val_accuracy: 0.8717
Epoch 14/20
30/30 [==============================] - 0s 14ms/step - loss: 0.0233 - accuracy: 0.9961 - val_loss: 0.4947 - val_accuracy: 0.8708
Epoch 15/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0195 - accuracy: 0.9963 - val_loss: 0.5319 - val_accuracy: 0.8692
Epoch 16/20
30/30 [==============================] - 0s 14ms/step - loss: 0.0147 - accuracy: 0.9980 - val_loss: 0.5663 - val_accuracy: 0.8682
Epoch 17/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0102 - accuracy: 0.9993 - val_loss: 0.6252 - val_accuracy: 0.8682
Epoch 18/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0069 - accuracy: 0.9997 - val_loss: 0.6526 - val_accuracy: 0.8669
Epoch 19/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0076 - accuracy: 0.9988 - val_loss: 0.6850 - val_accuracy: 0.8667
Epoch 20/20
30/30 [==============================] - 0s 13ms/step - loss: 0.0069 - accuracy: 0.9991 - val_loss: 0.7175 - val_accuracy: 0.8661

绘制训练损失和验证损失

import matplotlib.pyplot as plt

history_dict = history.history
loss_values = history_dict['loss']
val_loss_values = history_dict['val_loss']
epochs = range(1, len(loss_values) + 1)
plt.plot(epochs, loss_values, 'bo', label='Training loss') 
plt.plot(epochs, val_loss_values, 'b', label='Validation loss') 
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

out:img

绘制训练精度和验证精度

#清空图像
plt.clf() 
acc = history_dict['accuracy'] 
val_acc = history_dict['val_accuracy']
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

out:电影二分类图像2

训练损失每轮都在降低
训练精度每轮都在提升
这就是梯度下降优化的预期结果——你想要最小化的量随着每次迭代越来越小。
但验证损失和验证精度它们在第四轮达到最佳值
模型在训练数据上的表现越来越好
过拟合(overfit)
在第二轮之后,对训练数据过度优化,最终学到的表示仅针对于训练数据,无法泛化到训练集之外的数据。
在这种情况下,为了防止过拟合,你可以在 3 轮之后停止训练

从头开始重新训练一个模型

model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['accuracy'])
model.fit(x_train, y_train, epochs=4, batch_size=512)
results = model.evaluate(x_test, y_test)
out:
Epoch 1/4
49/49 [==============================] - 1s 9ms/step - loss: 0.4651 - accuracy: 0.8119
Epoch 2/4
49/49 [==============================] - 0s 9ms/step - loss: 0.2654 - accuracy: 0.9088
Epoch 3/4
49/49 [==============================] - 0s 9ms/step - loss: 0.2026 - accuracy: 0.9295
Epoch 4/4
49/49 [==============================] - 0s 8ms/step - loss: 0.1687 - accuracy: 0.9407
782/782 [==============================] - 2s 2ms/step - loss: 0.3145 - accuracy: 0.8772
in:
results
out:
[0.3144741356372833, 0.8772000074386597]
posted @ 2021-12-24 19:20  tifaIsMyWife  阅读(105)  评论(0)    收藏  举报