一篇关于《动手学深度学习》的读书笔记。

电子书地址:https://zh.gluon.ai/

环境准备

《动手学深度学习》基于Apache MXNet提供关于深度学习的Demo,并且提供了配套代码。整个项目通过conda的虚拟环境分发,并提供jupyter方式的可读文档。

安装方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 安装Mincode
sh Miniconda3-latest-Linux-x86_64.sh

# 下载工程文件 https://zh.d2l.ai/d2l-zh-1.0.zip ,并且解压到到d2l-zh文件夹
unzip d2l-zh-1.0.zip

# 安装d2l-zh项目的环境
source ~/.bashrc && conda env create -f environment.yml

# 切换到项目环境
conda activate gluon

# 打开项目jupyter
jupyter notebook --ip=$HOSTNAME --port=9999

# 其他一些命令

# 替换pip源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
# 删除pip源,恢复使用默认源
pip config unset global.index-url
# 配置国内MXNet站点的数据仓库
set MXNET_GLUON_REPO=https://apache-mxnet.s3.cn-north-1.amazonaws.com.cn/ jupyter notebook

conda

Conda是python的软件包管理软件,它和pip的最大不同在于同时提供虚拟环境管理功能,方便用户在多个虚拟环境中切换。

Miniconda是最小的conda安装环境,其中包含了Python2和Python3的运行环境。

Anaconda 是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项。

conda常用命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# envname 指代你新建的环境的名称
conda create --name envname
# 删除虚拟环境
conda remove --name envname --all
# 指定python版本
conda create -n envname python=3.4
# 指定python版本,以及多个包
conda create -n envname python=3.4 scipy=0.15.0 astroib numpy
# 给虚拟环境安装包
conda install -n envname scipy=0.15.0
# 从文件创建虚拟环境conda install pytorch torchvision cudatoolkit=9.0 -c pytorch
conda env create -f environment.yml
# 查看当前环境安装的包
conda list
# 查看所有环境conda install pytorch torchvision cudatoolkit=9.0 -c pytorch
conda info --envs
# 激活环境
conda activate envnameconda install pytorch torchvision cudatoolkit=9.0 -c pytorch
# 退出当前环境
conda deactivate

数据操作

在MXNet中,NDArray是一个类,也是存储和变换数据的主要工具,提供GPU计算和自动求梯度等多种功能。

NDArray 和 Numpy 有点类似,但是它可以表示多维张量,通过array函数和asnumpy函数可以使数据在NDArray和NumPy格式之间相互变换。

自动求梯度

MXNet使用autograd模块自动求梯度。

1
2
3
4
5
6
7
from mxnet import autograd,nd
x = nd.arange(4).reshape((4, 1))
x.attach_grad() # 调用attach_grad申请存储梯度所需要的内存
with autograd.record(): # 调用record函数后,MXNet会记录并计算梯度。
y = 2* nd.dot(x.T,x) # 目标函数是 y = x_T * x,我们要求这个函数在x = [0,1,2,3]_T时的梯度
y.backward()
print(x.grad) # x.grad 就是对应的梯度

参考mxnet api 查询

深度学习基础

线性回归

机器学习的几个概念:

  • 样本集、样本、标签
  • 模型、模型参数、损失函数
  • 优化算法:当模型没有解析解时,通过优化算法的有限次迭代求得一个使损失函数最小的数值解。
  • 学习率、批量大小 ——> 超参数
  • 模型预测 ~ 推断 ~ 测试

神经网络的几个概念:

  • 输入个数 ~ 特征数、特征向量维度
  • 神经元(计算单元)
  • 全连接层、稠密层

神经网络和线性回归的联系:

  • 线性回归是一个全连接单层神经网络

softmax回归

解决分类问题的思路:

  • 输出是离散值的分类问题,且输出个数等于标签类别数
  • 每个输出值是预测该类别的置信度,预测结果取最大输出值对应的类别

上述思路存在问题:

  • 输出值的范围不确定,难以直观判断这些值的含义

SoftMax回归的解决思路:

  • 将输出值变换成值为正且和为1的概率分布(归一化???实际上就是:每个输出求exp(y_i)/所有输出exp(y_i)之和)

交叉熵损失函数:

  • 分类问题的label一般这样表示:[1,0,0,0](四个类别时,分类取值为1)
  • 分类问题的模型输出表示为:[p1,p2,p3,p4](四个类别时,表示对应类别0~1之间的概率)
  • 分类问题不适合使用平方损失函数,适合使用交叉熵衡量损失。
  • 交叉熵损失函数和最大似然估计的思想一致。

分类的结果:

  • 准确率 = 正确预测数量 / 总预测数量

多层感知机

  • 不引入激活函数时,全连接多层感知机的模型本质上和单层感知机等价的,依然是线性模型。本质上全连接等价于仿射变换(一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。),多个仿射变换的叠加依然是仿射变换。

  • 解决上述问题:隐藏变量使用按元素运算的非线性函数进行变换,这些非线性函数称谓激活函数

    • ReLU(x)=max(x,0)
    • sigmoid函数:sigmoid(x)=1/(1+exp(−x))
    • tanh函数: tanh(x)=(1−exp(−2x))/(1+exp(−2x))

模型选择、欠拟合、过拟合

  • 当模型在训练数据集上更准确时,它在测试数据集上却不一定更准确:

    • 训练误差(training error):模型在训练数据集上表现出的误差
    • 泛化误差(generalization error):模型在任意一个测试数据样本上表现出的误差的期望,并常常通过测试数据集上的误差来近似。
  • K折交叉验证:把原始训练数据集分割成K个不重合的子数据集,然后我们做K次模型训练和验证。每一次,我们使用一个子数据集验证模型,并使用其他K−1个子数据集来训练模型。在这K次训练和验证中,每次用来验证模型的子数据集都不同。最后,我们对这K次训练误差和验证误差分别求平均。

  • 防止过拟合的方法:

    • 增大训练量
    • 使用复杂度合适的模型
    • 丢弃法:倒置丢弃法,原理是将隐藏层的输出丢弃或者拉伸。
    • 权重衰减:为模型添加损失函数惩罚项,使学出的模型参数值较小。原因是复杂模型往往权重参数很多,比较复杂。

权重衰减使,如何定义损失函数:

正向传播、反向传播和计算图

知道是什么东西就好?

数值稳定性和模型初始化

  • 数值稳定性:典型问题是衰减和爆炸,当神经网络层数较多时,模型的数值稳定性容易变差;
  • 通常需要随机初始化神经网络的模型参数,如权重参数。

kaggle赛题

房价预测,通过观摩别人的解题过程发现,大致流程如下:

  • 理解问题:理解数据的规模、指标的含义、数据有哪些确实等等。
  • 单因素研究:关注单一变量(如,输出)作图,并分析
  • 多因素研究:分析因变量和自变量之间的关系。
  • 基础清洗:清洗数据集并且对缺失数据,异常值和分类数据进行一些处理。
  • 检验假设:检查数据是否和多元分析方法的假设达到一致。
  • 建立模型,训练,预测

    MXNet 常用API接口

1
2
3
4
5
6
7
8
9
10
11
from mxnet.gluon import nn
def get_net():
# 定义一个神经网络模型,定义若干层隐藏层,并且指定隐藏层的激活类型, Dense表示定义一个全连接层。

net = nn.Sequential()
net.add(
nn.Dense(1024, activation='relu'),
nn.Dense(1)
)
net.initialize(init.Normal(sigma=0.01))
return net
1
2
3
4
from mxnet.gluon import loss as gloss
# 定义损失函数
loss = gloss.L2Loss() # 均方根损失
loss = gloss.SoftmaxCrossEntropyLoss() # 交叉熵损失
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from mxnet.gluon import data as gdata
from mxnet import autograd, gluon, init, nd

def train(net, train_features, train_labels, test_features, test_labels,
num_epochs, learning_rate, weight_decay, batch_size):
"""
训练函数
:param net :模型
:param train_features :训练集的特征
:param train_labels :训练集的标签
:param test_features :测试集的特征
:param test_labels :测试集的标签
:param num_epochs :在训练集上的训练次数,每次训练由多个小批量样本迭代组成
:param learning_rate :每次迭代的学习率
:param weight_decay :权重衰减系数,防止过拟合
:param batch_size :小批量样本迭代中的样本数目
:return :输出结果,即训练误差和测试误差(验证误差)
"""
train_ls, test_ls = [], []
train_iter = gdata.DataLoader( # 将训练集进行随机小批量分割
gdata.ArrayDataset(train_features, train_labels),
batch_size,
shuffle=True
)

trainer = gluon.Trainer( # 使用一个trainer,'adam'表示学习迭代的方法,同样还有sgd
net.collect_params(),
'adam',
{'learning_rate': learning_rate, 'wd': weight_decay}
)
for epoch in range(num_epochs):
for X, y in train_iter:
with autograd.record():
l = loss(net(X), y) # 求损失函数函数
l.backward() # 对损失函数的每个变量求偏导
trainer.step(batch_size) # 进行模型计算
train_ls.append(loss(net, train_features, train_labels))
if test_labels is not None:
test_ls.append(loss(net, test_features, test_labels))
return train_ls, test_ls

一些数据集

[MNIST]:手写数字识别数据集