OUC深度学习第1周学习记录:深度学习和pytorch基础

Part1 视频学习:绪论

1 人工智能和机器学习概述

人工智能(Artificial Intelligence):使一部机器像人一样进行感知、认知、决策、执行的人工程序或系统。

1.1 人工智能的三个层面

计算智能:能进行存储和计算

感知智能:具备类似于人的视觉、听觉、触觉等感知能力,能听会说,能看会认

认知智能:实现类似于人的认知能力,能理解、会思考、能产生对策,概念、意识、观念都是认知智能的表现

1.2 人工智能+

人工智能+金融:风控、征信、辅助交易等

人工智能+内容创作:自然语言处理、语音合成等

人工智能+机器人:视觉处理、语音识别、动作控制等

1.3 逻辑演绎vs归纳总结

1.4 知识工程vs机器学习

知识工程/专家系统:根据专家定义的知识和经验来进行推理和推断,从而模拟人类专家的决策过程,并解决问题

机器学习:机器基于有标注的数据进行自动训练

两者对比

知识工程的规则是手工设计的,可以人为控制,结果具有可解释性,但构建过程费时费力,并且无法消除专家主观经验带来的影响;

机器学习基于给出的数据进行自动学习,减少了人力消耗,提高了信息处理的效率,结果往往不易解释,但能有效减少主观因素的影响,具有较高的可信度 

1.5 机器学习的定义

最常用定义:计算机系统能够利用经验提高自身的性能

可操作定义:机器学习本质是一个基于经验数据的函数估计问题

统计学定义:提取重要模式、趋势,并理解数据,即从数据中学习

总而言之,机器学习就是从数据中自动提取知识的过程

1.6 用机器学习解决问题

先判断该情况下能否用机器学习:当问题规模较大,用到的准则复杂度较高,针对该问题有完备的可供训练的数据,具备有意义的模式,并且该问题没有解析解的情况下,可以考虑使用机器学习的方式去解决

机器学习的三个要素

  • 模型:对要学习问题映射的假设,即对问题建模并确定假设空间
  • 策略:从假设空间中学习或选择最优模型的准则,即确定目标函数
  • 算法:根据目标函数求解最优模型的具体计算方法,即确定模型参数

参数模型 非参数模型
优点 数据需求少,训练速度快 对数据适应性强,可拟合不同的函数形式
缺点 模型复杂度有限,与真实目标函数拟合度小 数据需求大,容易过拟合
判别模型 生成模型
优点

1. 节省计算资源

2. 数据需求小

3. 有助于简化学习问题

1. 提供更多信息(建模边缘分布→采样生成样本)

2. 样本量大时,能更快收敛到真实分布

3. 支持复杂训练情况(无监督训练、存在隐变量时)

缺点

1. 不能反映训练数据本身的特性

2. 先验结构具有不确定性

3. 变量间的关系不清楚,不可视

1. 数据需求大

2. 预测类问题准确率通常不如判别模型

2 深度学习概述

2.1 人工智能>机器学习>深度学习

2.2 传统机器学习vs深度学习

传统机器学习主要依靠人工设计特征,包括数据预处理、特征提取、特征转换等步骤,再基于特征训练得到分类器

深度学习对简单特征进行组合,得到更复杂的特征,从特征到输出的映射没有那么重要,因此深度学习又叫表示学习

2.3 神经网络结构的发展

 深度学习的三个助推剂:大数据、算法、计算力

2.4 深度学习的“不能”

  1. 稳定性低:算法输出不稳定,容易被“攻击”(对抗样本,单像素攻击)
  2. 可调试性差:模型复杂度高,难以纠错和调试
  3. 参数不透明:模型层级复合程度高,参数不透明
  4. 增量性差:端到端训练方式对数据依赖性强,模型增量性差(小样本问题)
  5. 推理能力差:专注直观感知类问题,对开放性推理问题无能为力
  6. 机器偏见:人类知识无法有效引入进行监督,机器偏见难以避免

解释性的三个层次: 

  • 找得到:能明确哪些特征对输出有重要影响,能准确快速纠错
  • 看得懂:算法能被人的知识体系所理解
  • 留的下:知识得到有效存储、积累和运用,越学越聪明

 2.5 连接主义+符号主义

  • 把知识图谱作为先验知识应用于深度学习
  • 基于深度学习来辅助知识图谱的构建

3 浅层神经网络

3.1 从生物神经元到M-P神经元模型

3.2 激活函数

可以使用激活函数实现非线性拟合

sigmoid函数存在的问题:①容易饱和;②输出不对称

3.3 感知器和神经网络

单层感知器:首个可以学习的人工神经网络

多层感知器的输出:g(W*x),其中g是激活函数,W是权重,x是输入

单层感知器无法实现异或

单隐层神经网络可视化:A Neural Network Playground (tensorflow.org)

万有逼近定理:如果一个隐层包含足够多的神经元,三层(单隐层)前馈神经网络(输入-隐层-输出)能以任意精度逼近任意预定的连续函数

  • 第一层感知器:空间变换
  • 第二层感知器:线性分类

双隐层感知器逼近非连续函数:当隐层足够宽时,双隐层感知器(输入-隐层1-隐层2-输出)可以逼近任意非连续函数,可以解决任何复杂的分类问题

神经网络的每一层都可以完成输入→输出空间变换,增加节点数可以增加线性转换能力,增加层数可以增加非线性转换次数

3.4 更宽or更深

在神经元数量相当的情况下,增加深度比增加宽度具有更强的网络表示能力,能产生更多的线性区域;深度的贡献是指数增长的,宽度的贡献是线性增长的

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

将多层神经网络看作一个复合的非线性多元函数,给定训练数据和对应的标签,训练过程就是让损失尽可能小的过程

参数沿负梯度方向更新可以使函数值下降

前馈神经网络的BP算法

3.6 梯度消失

前向传播和反向传播

以sigmoid激活函数为例,该函数在0处梯度最大,越往两边梯度越小,最后在饱和区趋近于0,当值落在饱和区时,梯度趋近于0,而反向传播时需要与梯度相乘,当梯度接近0时,反向传播得到的梯度也会接近0,此时梯度不会有明显变化,这就造成了梯度消失,不利于参数更新

增加深度更容易产生梯度消失,造成误差无法传播,并且多层网络容易陷入局部极值,难以找到更优解,难以训练,因此,在很长一段时间内,三层神经网络是主流,要想实现更深的神经网络,需要采取措施来抑制梯度消失,比如预训练、更改激活函数等

Part2 代码练习

1 pytorch基础练习

代码链接:(colab)pytorch基础练习

pytorch是一个python库,既可以进行GPU加速的张量计算,也可以构建基于反向自动求导系统的深度神经网络

import torch
torch.__version__
# out:1.11.0+cu113

pytorch基础练习:

1.1 使用torch.tensor定义数据

tensor:即张量,是数字各种形式的总称

import torch

# 一个数
x = torch.tensor(666)
print(x)
# out:
# tensor(666)

# 一维数组(向量)
x = torch.tensor([1,2,3,4,5,6])
print(x)
# out:
# tensor([1, 2, 3, 4, 5, 6])

# 二维数组(矩阵)
x = torch.ones(2,3)
print(x)
# out:
# tensor([[1., 1., 1.],
#         [1., 1., 1.]])

# 任意维度的数组(张量)
x = torch.ones(2,3,4)
print(x)
# out:
# tensor([[[1., 1., 1., 1.],
#          [1., 1., 1., 1.],
#          [1., 1., 1., 1.]],

#         [[1., 1., 1., 1.],
#          [1., 1., 1., 1.],
#          [1., 1., 1., 1.]]])

# tensor支持float32,float64,float16,uint8,int8,int16,int32,int64等多种类型的数据
# 创建tensor有多种方法,比如ones,zeros,eye,arange,linspace,rand,randn,normal,uniform,randperm

# 空张量
x = torch.empty(2,3)
print(x)
# out:
# tensor([[1.0409e-35, 0.0000e+00, 3.3631e-44],
#         [0.0000e+00,        nan, 2.3694e-38]])

# 随机初始化的张量
x = torch.rand(2,3)
print(x)
# out:
# tensor([[0.4104, 0.4116, 0.9312],
#         [0.6541, 0.3727, 0.3847]])

# long类型的全0张量
x = torch.zeros(2,3,dtype=torch.long)
print(x)
# out:
# tensor([[0, 0, 0],
#         [0, 0, 0]])

# 基于原有的tensor创建新tensor,可以利用dtype,device,size等属性信息
# new_*方法可以利用之前的dtype,device
y = x.new_ones(2,3)
print(y)
# out:
# tensor([[1, 1, 1],
#         [1, 1, 1]])

# 利用之前tensor的大小,并重新定义dtype
z = torch.randn_like(x,dtype=torch.float)
print(z)
# out:
# tensor([[ 1.3398,  0.4747,  1.7095],
#         [-2.5772,  0.3592, -1.5591]])

1.2 定义操作

用tensor可以进行以下计算:

  • 基本运算:加减乘除,求幂取余,包括abs/sqrt/div/exp/fmod/pow,及一些三角函数cos/sin/asin/atan2/cosh,及ceil/round/floor/trunc等
  • 布尔运算:大于小于,最大最小,包括gt/lt/ge/le/eq/ne,topk,sort,max/min等
  • 线性计算:矩阵乘法,求模,求行列式,包括trace,diag,mm/bmm,t,dot/cross,inverse,svd等
# 创建一个2*4的tensor
m = torch.Tensor([[1,2,3,4],[3,4,5,6]])
print(m.size(0),m.size(1),m.size(),sep='--')
# out:2--4--torch.Size([2, 4])

# 返回m的元素数量
print(m.numel())
# out:8

# 返回第0行第2列的数
print(m[0][2])
# out:tensor(3.)

# 返回第1列的全部元素
print(m[:,1])
# out:tensor([2., 4.])

# 返回第0行的全部元素
print(m[0,:])
# out:tensor([1., 2., 3., 4.])

# 创建一个从1到5的tensor(这里没有5)
v = torch.arange(1,5)#Long
print(v)
# out:tensor([1, 2, 3, 4])

# scalar product
v = torch.arange(1,5,dtype=torch.float32)#float32
print(m @ v)
# out:tensor([30., 50.])

# 1*1+2*2+3*3+4*4
print(m[[0],:] @ v)
# out:tensor([30.])

# 和一个2*4的随机tensor相加
print(m+torch.rand(2,4))
# out:
# tensor([[1.8033, 2.4563, 3.9932, 4.6722],
#         [3.0424, 4.2175, 5.1733, 6.7986]])

# 转置
print(m.t())
print(m.transpose(0,1))
# out:
# tensor([[1., 3.],
#         [2., 4.],
#         [3., 5.],
#         [4., 6.]])

# returns a 1D tensor of steps equally spaced points between start=3, end=8 and steps=20
print(torch.linspace(3,8,20))
# out:
# tensor([3.0000, 3.2632, 3.5263, 3.7895, 4.0526, 4.3158, 4.5789, 4.8421, 5.1053,
#         5.3684, 5.6316, 5.8947, 6.1579, 6.4211, 6.6842, 6.9474, 7.2105, 7.4737,
#         7.7368, 8.0000])
from matplotlib import pyplot as plt

# matlabplotlib 只能显示numpy类型的数据,下面展示了转换数据类型,然后显示
# 注意 randn 是生成均值为 0, 方差为 1 的随机数
# 下面是生成 1000 个随机数,并按照 100 个 bin 统计直方图
plt.hist(torch.randn(1000).numpy(), 100);
# 当数据非常非常多的时候,正态分布会体现的非常明显
plt.hist(torch.randn(10**6).numpy(), 100);

from numpy.ma.core import outer
# 创建两个1*4的tensor
a = torch.Tensor([[1, 2, 3, 4]])
b = torch.Tensor([[5, 6, 7, 8]])

# 在0方向拼接(即在Y方向上拼接),得到2*4的矩阵
print(torch.cat((a,b),1))
# out:
# tensor([[1., 2., 3., 4., 5., 6., 7., 8.]])

2 螺旋数据分类

代码链接:(colab)螺旋数据分类​​​​​​​​​​​​

2.1 下载绘图函数到本地

# 下载绘图函数到本地
!wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py

2.2 引入基本的库并初始化重要参数

# 引入用到的库并初始化重要参数
import random
import torch
from torch import nn,optim
import math
from IPython import display
from plot_lib import plot_data,plot_model,set_default

# 在GPU上运行torch
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('device:',device)

# 初始化随机数种子,使得结果能复现
seed = 616
random.seed(seed)
torch.manual_seed(seed)

N = 1000  # 每类样本的数量
D = 2 # 每个样本的特征维度
C = 3 # 样本的类别
H = 100 # 神经网络里隐层单元的数量

初始化X和Y,X可以理解为特征矩阵,Y可以理解为样本标签,X是一个N*C行D列的矩阵 在python中,调用zeros等类似函数时,第一个参数是矩阵的行,y方向,第二个参数是矩阵的列,x方向

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个数
    # 根据公式计算出三类样本,构成螺旋形
    # torch.randn(N):得到N个均值为0,方差为1的一组随机数
    inner_var = torch.linspace((2*math.pi/C)*c,(2*math.pi/C)*(2+c),N)+torch.randn(N)*0.2
    # 将每个样本的坐标保存在X里
    # Y里存储样本的类别,分别为[0,1,2]
    for xi in range(N*c,N*(c+1)):
        X[xi] = t[index] * torch.FloatTensor((math.sin(inner_var[index]),math.cos(inner_var[index])))
        Y[xi] = c
        index += 1

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

plot_data(X,Y)

2.3 构建线性模型分类

lr = 1e-3
lamda = 1e-5
epoch = 1000  # 训练轮数

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

# 交叉熵损失函数
loss_fn = torch.nn.CrossEntropyLoss()

# # 随机梯度下降优化器SGD
# optimizer = torch.optim.SGD(model.parameters(),lr=lr,weight_decay=lamda)
# adam优化器
optimizer = torch.optim.Adam(model.parameters(),lr=lr,weight_decay=lamda)

# 训练
for i in range(epoch):
  # 得到预测结果
  y_pred = model(X)
  # 计算损失和准确率
  loss = loss_fn(y_pred,Y)
  score,pred = torch.max(y_pred,1)  # 沿着X方向取最大值score和最大值对应位置pred
  acc = (Y==pred).sum().float()/len(Y)
  print('EPOCH:%i, LOSS:%.6f, ACCURACY:%.3f'%(i,loss.item(),acc))
  display.clear_output(wait=True) # 清除输出,只展示当前轮的训练效果

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

print(y_pred.shape) # 查看模型的预测结果
print(y_pred[10,:]) #查看第10行预测结果
print(score[10])  #查看第10行最大值
print(pred[10]) #查看第10行预测结果

print(model)  # 查看模型
plot_model(X, Y, model) #查看分类后的结果

使用Adam优化器,最后一轮训练结果如下:

 由此可见,对于这种较复杂的数据分布,只靠线性模型很难达到准确分类,1000轮后的准确率也只在50%左右

2.4 构建两层神经网络分类

 在之前的线性模型的基础上添加ReLU激活函数

lr = 1e-3
lamda = 1e-5
epoch = 1000  # 训练轮数

# 创建线性模型,每一个线性模型都包含weight和bias
model = nn.Sequential(
    nn.Linear(D,H),
    nn.ReLU(),  # 添加了激活函数
    nn.Linear(H,C)
)
# 把模型放到GPU上
model.to(device)

# 交叉熵损失函数
loss_fn = torch.nn.CrossEntropyLoss()

# # 随机梯度下降优化器SGD
# optimizer = torch.optim.SGD(model.parameters(),lr=lr,weight_decay=lamda)
# adam优化器
optimizer = torch.optim.Adam(model.parameters(),lr=lr,weight_decay=lamda)

# 训练
for i in range(epoch):
  # 得到预测结果
  y_pred = model(X)
  # 计算损失和准确率
  loss = loss_fn(y_pred,Y)
  score,pred = torch.max(y_pred,1)  # 沿着X方向取最大值score和最大值对应位置pred
  acc = (Y==pred).sum().float()/len(Y)
  print('EPOCH:%i, LOSS:%.6f, ACCURACY:%.3f'%(i,loss.item(),acc))
  display.clear_output(wait=True) # 清除输出,只展示当前轮的训练效果

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

print(model)  # 查看模型
plot_model(X, Y, model) #查看分类后的结果

使用Adam优化器,最后一轮训练结果:

加入激活函数之后,模型具有了一定的非线性拟合能力,准确率能达到95.4%,相比单纯的线性模型有了很大提高

2.5 发现的问题

优化器的选取:

①在使用SGD优化器的情况下,加入激活函数后见效缓慢,在其他参数不变的情况下,在1000轮内无法达到比较满意的准确率

 ②在使用Adam优化器的情况下,未添加激活函数时几乎没有训练效果,准确率和损失都没有明显变化,疑似发生了梯度消失的现象;但在添加激活函数之后,在1000轮内即可达到0.95以上的准确率

加入激活函数前:

 加入激活函数后:

综上,在本次实验中,Adam优化器比SGD优化器更容易取得更好的训练效果,SGD优化器需要更长的训练周期

激活函数的选取:在使用Adam优化器的情况下,选取不同的激活函数测试5000轮内的训练效果

①sigmoid()

LOSS:0.076154, ACCURACY:0.986000

②tanh()

LOSS:0.023085, ACCURACY:0.999000

③ReLU()

LOSS:0.018443, ACCURACY:0.999333

④LeakyReLU()

LOSS:0.016924, ACCURACY:0.999000

 综上,在这四种激活函数中,sigmoid()效果最差,ReLU()和LeakyReLU()效果最好

2.6 总结

不同的优化器或激活函数会带来不一样的训练效果,需要多尝试和探索,根据实际问题使用合适的函数

猜你喜欢

转载自blog.csdn.net/qq_55708326/article/details/125736182