机器学习笔记-Week01

深度学习和pytorch基础

文章目录

  • 深度学习和pytorch基础
    • 机器学习的模型分类
      • 监督学习
      • 无监督学习
      • 半监督学习
      • 强化学习
      • 参数模型
      • 非参数模型
      • 生成模型
      • 判别模型
    • 神经网络基础
      • 单层感知器
      • 万有逼近定理
      • 神经网络每一层的作用
      • 神经网络的参数学习:误差反向传播
      • 深层神经网络的问题:梯度消失
      • 逐层预训练
      • 受限玻尔兹曼机和自编码器
      • 解决梯度消失
    • 利用人工神经网络解决监督学习分类问题
      • 构建螺旋分类模型
      • 构建线性模型分类
      • 加入激活函数构建两层神经网络分类

机器学习是实现人工智能的手段,其目的在于使得程序有能够利用过往经验自主学习,优化答案

而深度学习是实现机器学习的一种具体方法

机器学习的模型分类

对于一个具体的问题,可以通过模型->策略 ->算法进行求解

对于数据分类问题,我们可以将模型分为监督学习模型和无监督学习模型

监督学习

顾名思义,监督学习是基于人为标记监督的,在数据中,通过数据学习标记分界面,可以利用决策函数达到预测分析的目的

无监督学习

无监督学习由于没有对数据进行标记,对于数据只有不同的分布,而它只能从数据分布中产生描述数据的功能

半监督学习

半监督学习对于部分数据进行标记,可以减少人为的工作

强化学习

强化学习像是对无监督学习的一种升级,在无监督学习结果的基础上进行人为反馈结果

参数模型

基于数据分布假设,可以 通过足量数据推出概率分布函数

非参数模型

数据本身带有统计特性,非参数模型自适应数据,没有具体的数据分布参数

生成模型

生成模型是对于监督学习模型的数据作为对象,利用联合分布概率 P ( X , Y ) P(X,Y) P(X,Y)建模,生成数据对应的函数结果

判别模型

判别模型是在已知函数的情况,对数据结果进行预测

在过去的机器学习中,需要大量的数据和人工反馈结果,导致机器学习的耗时和结果具有局限性,而现在我们希望利用尽可能少量的数据交给机器进行自主学习。然而机器学习由于是高度依赖数据的,因此仍然存在一些问题,例如容易被噪点污染攻击,缺乏联想推理能力,易被具有极端特性的数据影响,难以确定错误等等。

神经网络基础

人们受到神经元多输入单输出,时间和空间不同步等等特性的启发,利用数学语言描述了一种函数方法,称之为激活函数

单层感知器

激活函数就相当与一种处理函数,当函数在什么条件下被激活就类比于在什么样的输入下能够有着什么样的输出,对于不同的输出,我们可以人为定义这些输出符号所对应的特殊含义。非线性的激活函数可以帮助我们对数据有着更多的情况处理。

由于单个激活函数的功能局限,它只能把一个逻辑空间化为有限部分,分类作业具有局限性,但是对于非线性问题,单层感知器无法解决。在逻辑空间中,由于激活函数相当于逻辑空间中的一条线,所有对于非线性的空间划分(即需要多条线的划分)的问题,急需要利用多层感知器实现,类比于数字逻辑中的一些列复杂逻辑都可以利用与或非门的逻辑组合来实现。利用激活函数,人们构建了从逻辑数学到数字数学的桥梁。

万有逼近定理

如果一个隐层包含足够多的神经元,三层前馈神经网络能以任意精度逼近任意预定的连续函数

万有逼近定理启示我们对于任意复杂问题利用神经网络都能建模

神经网络每一层的作用

每一层数学公式 y → = a ( W ⋅ x → + b ) \overrightarrow{y}=a(W·\overrightarrow{x}+b) y =a(Wx +b)

利用线性代数的空间解释可以视为对矩阵空间进行一系列的空间变换,乘以一个矩阵是对空间的扭曲变换,加上常数可视为对空间的平移

对于那么类比于神经网络的层数,即增加激活函数的次数,激活函数是非线性函数,即对空间有了更多区域划分

而节点则可对空间进行一系列升降维空间转换等操作。

利用神经网络,我们可以将原始数据各自映射到线性可分的空间结果上,从而对数据进行分类预测等操作

神经网络的**深度(层数)宽度(节点数)**对于空间的贡献是不同的,深度的贡献是指数级的,而宽度的贡献是线性的

神经网络的参数学习:误差反向传播

我们希望得到一个神经网络模型,对于一个多层神经网络,我们可以利用一个复合的非线性多元函数去描述,那么这个多元函数的参数如何取确定呢?我们可以利用误差反向传播这样一个机制。

给定训练数据 { x i , y i } {{x^i,y^i}} xi,yi,我们会产生结果,并将结果回传,我们希望产生结果数据和真实值的误差尽可能的小。

回转和更新神经网络的机制就是利用梯度

参数会根据梯度进行调整不断逼近使得参数误差变小

深层神经网络的问题:梯度消失

我们知道我们的误差反馈将利用梯度传播,但由于复合函数的特性,使得部分梯度在传播的过程中大幅变小,造成梯度消失这样一个问题

为了解决这样一个问题,我们可以使用逐层预训练的方法

逐层预训练

对于每一层神经网络的参数,我们采用逐层预训练的方式就避免了传递过程中的梯度消失

受限玻尔兹曼机和自编码器

在逐层预训练的过程中由于缺乏对照的正确的实现结果(没有隐层结果),利用自编码器将结果进行解码在与输入进行对比来逐步确定参数。利用堆叠自编码器进行全网络微调

解决梯度消失

利用无监督数据进行分层预训练

用更好的激活函数,避免某些函数的梯度消失

利用辅助损失函数

利用人工神经网络解决监督学习分类问题

我们给定一个螺旋模型,样本由3部分组成,我们将基于该模型对图中的任意一点进行预测划分

机器学习笔记-Week01_第1张图片
带有高斯噪点的螺旋图

带有高斯噪点的模型图更接近于真实数据

我们可以观察到对于图中的三个样本并不是线性可分的,按照线性划分的方法对于其中的某一块区域存在着不同的样本

为了解决这样的问题我们可以利用两种方式,一种是将数据通过空间变换转成线性可分的,另一种是利用神经网络得到合适的非线性数据划分

构建螺旋分类模型

二维坐标中的每个点相当于一个向量,有三个样本,每个样本有1000个二维向量

这是模型的输入,构造一个3000行2维的向量构建螺旋空间

并对每个向量进行标记对应的种类

X = torch.zeros(N * C, D).to(device)
Y = torch.zeros(N * C, dtype=torch.long).to(device)
for c in range(C):
    index = 0
    t = torch.linspace(0, 1, N) # 在[0,1]间均匀的取10000个数,赋给t
    # 下面的代码不用理解太多,总之是根据公式计算出三类样本(可以构成螺旋形)
    # torch.randn(N) 是得到 N 个均值为0,方差为 1 的一组随机数,注意要和 rand 区分开
    inner_var = torch.linspace( (2*math.pi/C)*c, (2*math.pi/C)*(2+c), N) + torch.randn(N) * 0.2
    
    # 每个样本的(x,y)坐标都保存在 X 里
    # Y 里存储的是样本的类别,分别为 [0, 1, 2]
    for ix in range(N * c, N * (c + 1)):
        X[ix] = t[index] * torch.FloatTensor((math.sin(inner_var[index]), math.cos(inner_var[index])))
        Y[ix] = c
        index += 1

print("Shapes:")
print("X:", X.size())
print("Y:", Y.size())

构建线性模型分类

形如 y = w x + b y=wx+b y=wx+b 的线性函数我们利用线性函数对数据进行训练,则需要一个损失函数计算,并通过梯度反向传播对我们的线性函数进行优化

learning_rate = 1e-3
lambda_l2 = 1e-5

# nn 包用来创建线性模型
# 每一个线性模型都包含 weight 和 bias
model = nn.Sequential(
    nn.Linear(D, H),
    nn.Linear(H, C)
)
model.to(device) # 把模型放到GPU上

# nn 包含多种不同的损失函数,这里使用的是交叉熵(cross entropy loss)损失函数
criterion = torch.nn.CrossEntropyLoss()

# 这里使用 optim 包进行随机梯度下降(stochastic gradient descent)优化
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=lambda_l2)

# 开始训练
for t in range(1000):
    # 把数据输入模型,得到预测结果
    y_pred = model(X)
    # 计算损失和准确率
    loss = criterion(y_pred, Y)
    score, predicted = torch.max(y_pred, 1)
    acc = (Y == predicted).sum().float() / len(Y)
    print('[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f' % (t, loss.item(), acc))
    display.clear_output(wait=True)

    # 反向传播前把梯度置 0 
    optimizer.zero_grad()
    # 反向传播优化 
    loss.backward()
    # 更新全部参数
    optimizer.step()

我们可以看到在训练部分的代码中,我们会计算损失和准确率并输出对应参数

在反向传播的过程中有一个梯度置0操作,这是因为PyTorch会默认对梯度进行累加(据说这样是因为可以降低对GPU的要求)

[EPOCH]: 999, [LOSS]: 0.864019, [ACCURACY]: 0.500

我们可以看到准确率只有50%

机器学习笔记-Week01_第2张图片

观察线性模型可知这两层对应于我们模型的两个输入,第一层输入是100个样本的神经节点,利用的数据是生成的3000个二维向量,第二层是我们的监督数据,也就是我们对应的人工划分。

可以看出这就是一个简单的监督学习分类的线性模型,而该模型的划分从图中我们可以看出来并不是很准确的

加入激活函数构建两层神经网络分类

learning_rate = 1e-3
lambda_l2 = 1e-5

# 这里可以看到,和上面模型不同的是,在两层之间加入了一个 ReLU 激活函数
model = nn.Sequential(
    nn.Linear(D, H),
    nn.ReLU(),
    nn.Linear(H, C)
)
model.to(device)

# 下面的代码和之前是完全一样的,这里不过多叙述
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=lambda_l2) # built-in L2

# 训练模型,和之前的代码是完全一样的
for t in range(1000):
    y_pred = model(X)
    loss = criterion(y_pred, Y)
    score, predicted = torch.max(y_pred, 1)
    acc = ((Y == predicted).sum().float() / len(Y))
    print("[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f" % (t, loss.item(), acc))
    display.clear_output(wait=True)
    
    # zero the gradients before running the backward pass.
    optimizer.zero_grad()
    # Backward pass to compute the gradient
    loss.backward()
    # Update params
    optimizer.step()

可以看出,我们在两层模型中加入了ReLu()激活函数

机器学习笔记-Week01_第3张图片

加入激活函数,意味着模型具有了处理非线性划分的能力,我们观察训练之后的结果

可以看出准确率得到了很大的提高

[EPOCH]: 999, [LOSS]: 0.178409, [ACCURACY]: 0.949

机器学习笔记-Week01_第4张图片

模型的层数也变成了一个输入层和两个处理的神经网络,我们可以看到划分在局部边缘虽然还是有一些不准确,这可能是我们数据量太少造成的,不过整体上的准确率已经到了一个很高的水平

参考资料

Artificial neural networks

中国海洋大学视觉实验室前沿理论小组 PyTorch 学习

Week 2 – Practicum: Training a neural network

你可能感兴趣的:(机器学习,机器学习,人工智能,深度学习)