Skip to content

深度学习实践基础

原理

深度学习本质是构建一个具有非常多参数的函数,通过大量数据的训练,使得这个函数能够拟合出数据的分布规律,从而实现对数据的预测。由于参数远远大于人能够总结理解的规律,因而能够实现对一些人类无法理解的复杂规律的预测。

正是因为其本质是极大量参数的一个函数,因此深度学习的训练过程也是一个函数优化的过程,即通过不断调整函数的参数,使得函数的输出与真实值之间的误差最小。同时,也正因如此,深度学习模型只能用于识别或预测规律相同的数据(因为你找到的是一个符合数据规律的函数),对于不符合你训练数据规律的输入数据将无能为力。

我们构建深度学习神经网络的过程,就是在通过不断调节函数的结构,使之能够更好地适应数据结构,也是在告诉计算机选择哪种数据处理和选择参数的方式。实际上在工程实践中,我们并没有一个确定的方法来构建一个神经网络,而是通过不断尝试不同的结构和参数,来找到一个最适合的模型。

基础概念

数据集

即用来训练模型的数据,一般分为训练集、验证集和测试集。训练集用来训练模型,验证集用来调整模型的参数,测试集用来评估模型的性能。在监督学习中,数据集一般包括输入数据和对应数据的标签(例如数据的分类情况)。

损失函数

损失函数是用来衡量模型输出值与真实值之间的差距的函数,一般用来指导模型的训练。常见的损失函数有均方误差、交叉熵等。

交叉熵:'CrossEntropy',是一种常用的损失函数,用来衡量两个概率分布之间的差异。在分类问题中,交叉熵用来衡量模型输出的概率分布与真实标签的概率分布之间的差异。

均方误差:'MSE',是另一种常用的损失函数,用来衡量模型输出值与真实值之间的差距。在回归问题(预测线性数值而非分类的问题)中,均方误差用来衡量模型输出值与真实值之间的差距。

优化器

优化器是用来调整模型参数的算法,简单来说便是从随机一组参数开始训练,训练一轮后通过优化器来尽量使参数往损失较小的方向靠近,一般用来最小化损失函数。常见的优化器有RMSProp、SGD、Adam等。

SGD:随机梯度下降,是最基础的优化器,通过计算损失函数对参数的梯度(即通过将每层网络的权重往梯度相反方向移动),来调整参数。学习率是一个重要的参数,用来控制每次参数调整的步长,学习率过小会导致训练过慢,学习率过大会导致训练不稳定。

RMSProp:RMSProp是一种自适应学习率的优化器,通过计算梯度的平方的指数加权移动平均来调整学习率,从而使得学习率在训练过程中自适应调整。可以当作SGD的改进版。

Adam:Adam是一种自适应学习率的优化器,通过计算梯度的一阶矩估计和二阶矩估计来调整学习率,从而使得学习率在训练过程中自适应调整。可以当作RMSProp的改进版。

神经网络

神经网络是一种模拟人类神经元工作方式的模型,通过多层神经元的连接,来实现对数据的处理。神经网络的基本结构包括输入层、隐藏层和输出层,其中输入层用来接收数据,输出层用来输出模型的预测结果,隐藏层用来处理数据。

通道

表明当前层数据的纬度数量,通道越多表明具有更复杂的参数特征。

通道数目的选择一般是根据数据的复杂度来选择的,一般来说,数据越复杂,通道数目越多。然而,通道数目过多容易导致模型过拟合,通道数目过少容易会导致模型欠拟合。

激活函数

激活函数用来控制当前网络层的数据输入到输出的变换方式。一般来说,这个激活函数应该是一个非线性的函数,因为多个线性的函数实际上可以合并为一个线性函数,导致失去多个网络层的意义。

relu:常用的激活函数,适用于大部分情况,在输入大于0时输出与权重向量计算后的值,在输入小于0时输出0,需要注意的是relu函数在输入小于0时会导致梯度消失。

sigmoid:常用的输出层激活函数,适用于二分类问题、多分类多标签问题、回归到0-1问题,将输入值映射到0-1之间,可以用来表示概率。

tanh:常用的输出层激活函数,适用于二分类问题,将输入值映射到-1-1之间,可以用来表示概率。

softmax:常用的输出层激活函数,适用于多分类单标签问题,将输入值映射到0-1之间,可以用来表示概率。

常用的表示层

卷积层:Convolutional Layer,用来提取输入数据的特征,一般用来处理图像数据。卷积核大小是一个重要参数,用来控制特征提取的粒度。

池化层:Pooling Layer,用来降低数据的维度,同时保留卷积层提取的特征,一般用来减少数据量,提高计算效率。

全连接层:Fully Connected Layer,用来将数据映射到输出层,一般用来处理分类问题。

循环层:Recurrent Layer,用来处理序列数据,一般用来处理文本数据(或其他输入数据具有时序特征的)。

归一化层:Normalization Layer,用来将数据进行归一处理,提高模型的稳定性,一般用来处理训练过程中的梯度消失问题。

Dropout层:Dropout Layer,用来随机丢弃一部分神经元,防止模型过拟合。rate参数用来控制丢弃的比例,一般在0.2-0.5之间。

训练

batch-size:批处理大小,用来控制每次训练的数据量,一般用来控制训练的速度,一般在32-128之间。

epoch:训练轮数,用来控制训练的次数,一般用来控制训练的效果,一般在10-100之间。

验证评估

验证集

验证集往往是从训练集中划分出来的一部分数据,用来调整模型的参数,一般用来防止模型过拟合。

我们常常会使用matplotlib来绘制模型的训练过程(每一轮次的训练效果和验证集的验证效果),从而可以直观地看到模型的训练效果。

为什么要划分验证集:因为模型在训练过程中,会不断调整参数,使得模型在训练集上的表现越来越好,但是这并不代表模型在未知数据上的表现也会越来越好,因此需要一个独立的数据集来评估模型的泛化能力。而验证集与测试集的区别在于,验证集是用来在训练过程中不断调整模型参数的,而测试集是用来最终评估模型性能的。

几种常见的划分数据集的方法:

留出法:将数据集划分为训练集和测试集,一般比例为8:2

k折交叉验证法:将数据集划分为k份,每次取其中一份作为验证集,其余作为训练集,重复k次,取平均值。适用于数据集较小的情况。

打乱数据的k折交叉验证法:每一轮都将数据集打乱后再进行k折交叉验证。适用于数据集较小而又需要有较高准确率的情况,由于每轮都有k次训练计算,计算代价很大。

测试集

测试集是用来评估模型性能的数据集,一般用来评估模型的泛化能力。测试集应选取与训练集和验证集尽量不同的且具有代表性的数据,以保证模型的泛化能力。

评估指标

准确率:Accuracy,用来衡量模型的预测准确率,即模型预测正确的样本数占总样本数的比例。

召回率:Recall,用来衡量模型的预测准确率,即真正为正样本中模型预测为正样本的比例。

loss:损失,用来衡量模型的预测误差,即模型预测值与真实值之间的差距。

MSE:均方误差,用来衡量模型的预测误差,即模型预测值与真实值之间的差距的平方和。

过拟合

过拟合是指模型在训练集上表现很好,但在测试集上表现很差的现象,一般是因为模型过于复杂,导致模型过度拟合训练集数据。过拟合的解决方法一般有:

增加数据量:增加数据量可以减少模型的过拟合,因为模型可以学习到更多的数据规律。

减少模型复杂度:减少模型的复杂度可以减少模型的过拟合,因为模型的复杂度越低,越不容易过拟合。

正则化:正则化是一种用来减少模型过拟合的方法,通过在损失函数中加入正则项,来惩罚模型的复杂度。

Dropout:Dropout是一种用来减少模型过拟合的方法,通过随机丢弃一部分神经元,来防止模型过拟合。

实践

我们尝试使用tensorflow来实现一个简单的深度学习模型,用来识别手写数字。

python
import tensorflow as tf
from tensorflow.keras import layers, models
from keras.datasets import mnist
import matplotlib.pyplot as plt

# 加载数据
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 数据预处理
x_train, x_test = x_train.reshape((60000, 28 * 28)), x_test.reshape((10000, 28 * 28))
x_train, x_test = x_train.astype('float32') / 255.0, x_test.astype('float32') / 255.0
y_train, y_test = tf.keras.utils.to_categorical(y_train, 10), tf.keras.utils.to_categorical(y_test, 10) # one-hot编码

# 划分验证集
val_x, val_y = x_train[:10000], y_train[:10000]
train_x, train_y = x_train[10000:], y_train[10000:]

# 构建模型
network = models.Sequential()
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,))) # 全连接的输入层
network.add(layers.Dropout(0.3)) # Dropout层,防止过拟合
network.add(layers.Dense(10, activation='softmax')) # 全连接的输出层,输出10个数字类别

# 编译模型
network.compile(
    optimizer='rmsprop', 
    loss='categorical_crossentropy',
    metrics=['accuracy'],
    validation_data=(val_x, val_y)
)

# 训练模型
history = network.fit(train_x, train_y, epochs=5, batch_size=128)

# 绘制训练过程

plt.plot(history.history['accuracy'], 'bo', label='accuracy')
plt.plot(history.history['val_accuracy'], 'b', label = 'val_accuracy')
plt.title('Training and validation accuracy')
plt.legend()
plt.show()

# 评估模型
test_loss, test_acc = network.evaluate(x_test, y_test)
print('test_acc:', test_acc)
print('test_loss:', test_loss)