目录
part1:理论视频学习
一.人工智能和深度学习概述
1.人工智能定义及其三个层面
2.机器学习
2.1 机器学习定义
2.2 机器学习过程
2.2.1 模型分类
二 .深度学习概述
1.人工智能、机器学习、深度学习之间的关系
2.深度学习的不能
3.浅层神经网络
3.1激活函数
3.2万有逼近定理与神经网络
3.3更宽or更深
3.4反向传播误差
3.5梯度消失
part2:代码练习
一.pytorch基础练习
1.定义数据
2.定义操作
二.螺旋曲线分类
2.2构建线性模型分类
2.3两层神经网络分类
小结
part1:理论视频学习
一.人工智能和深度学习概述
1.人工智能定义及其三个层面
人工智能(Artificial Intelligence) 使一部机器像人一样进行感知、认知、决策、执行的人工程序或系统。
计算智能:能进行存储和计算
感知智能:具备类似于人的视觉、听觉、触觉等感知能力,能听会说,能看会认
认知智能:实现类似于人的认知能力,能理解、会思考、能产生对策,概念、意识、观念都是认知智能的表现
2.机器学习
2.1 机器学习定义
最常用定义:计算机系统能够利用经验提高自身的性能
可操作定义:机器学习本质是一个基于经验数据的函数估计问题
统计学定义:提取重要模式、趋势,并理解数据,即从数据中学习
总之,机器学习就是从数据中自动提取知识的过程
2.2 机器学习过程
2.2.1 模型分类
数据标记:监督学习模型、非监督学习模型
数据分布:参数模型、非参数模型
建模对象:判别模型、生成模型
数据标记
无监督学习:样本没有标记。无监督学习从数据中学习模式,适用于描述数据,目的在于发现数据中模式/有意义信息。
监督学习:样本具有标记(输出目标)。监督学习从数据中学习标记分界面 (输入-输出的映射函数),适用于预测数据标记。
半监督学习:部分数据标记已知,是监督学习和无监督学习的混合。
强化学习:数据标记未知,但 知道与输出目标相关的反馈,适用决策类问题。
数据分布
参数模型: 对数据分布进行假设,待求解的数据模式/映射可以用一组有限且固定数目 的参数进行刻画 。
非参数模型: 不对数据分布进行假设,数据的所有统计特性都来源于数据本身。
参数模型 | 非参数模型 | |
优点 | 数据需求少、训练快速 | 对数据适应性强,可拟合不同的函数形式 |
缺点 | 模型复杂度有限,与真实目标函数拟合度小 | 数据需求大、容易过拟合 |
建模对象
生成模型: 对输入和输出的联合分布(, )建模
判别模型: 对已知输入条件下输出Y的条件分布(|) 建模
生成模型 | |
优点 | 提供更多信息(建模边缘分布-->采样生成样本) 样本量大时,更快收敛到真实分布 支持复杂训练情况(无监督训练、存在隐变量时) |
缺点 | 数据需求大 预测类问题准确率通常不如判别模 |
二 .深度学习概述
1.人工智能、机器学习、深度学习之间的关系
人工智能>机器学习>深度学习
2.深度学习的不能
1.算法输出不稳定,容易被“攻击”(对抗样本,单像素攻击)
2.模型复杂度高,难以纠错和调试
3.模型层级复合程度高,参数不透明
4.端到端训练方式对数据依赖性强,模型增量性差(小样本问题)
5.专注直观感知类问题,对开放性推理问题无能为力
6.人类知识无法有效引入进行监督,机器偏见难以避免
3.浅层神经网络
3.1激活函数
激活函数 : 神经元继续传递信息、产生 新连接的概率(超过阈值被 激活,但不一定传递)
没有激活函数相当于矩阵相乘 ,通过激活函数可以实现非线性拟合。
常见激活函数 :
3.2万有逼近定理与神经网络
万有逼近定理:如果一个隐层包含足够多的神经元,三层前馈神经网络(输入-隐层 -输出)能以任意精度逼近任意预定的连续函数。
神经网络每一层的数学公式
神经网络学习如何利用矩阵的线性变换加激活函数的非线性变换, 将原始输入空间投影到线性可分的空间去分类/回归。
增加节点数:增加维度,即增加线性转换能力。
增加层数:增加激活函数的次数,即增加非线性转换次数
3.3更宽or更深
在神经元总数相当的情况下,增加网络深度可以比增加宽度带来更强的网络表示能力:产生更多的线性区域。
深度和宽度对函数复杂度的贡献是不同的,深度的贡献是指数增长的,而宽度的贡献是线性的.
3.4反向传播误差
3.5梯度消失
由于误差通过梯度传播,当梯度变得接近于0时,由于反向传播将与梯度相乘,得到的新梯度也接近为0,会使得梯度消失,误差无法传播,使数据得不到及时更新。
part2:代码练习
一.pytorch基础练习
1.定义数据
一般定义数据使用torch.Tensor , tensor的意思是张量,是数字各种形式的总称
import torch
# tensor(data: Any, dtype: Optional[_dtype]=None, ... , requires_grad: _bool=False)
# 可以看出,data是任意数据,可以为一个数,一个list,二维数组等等
x = torch.tensor(666) # 标量
print(x) # tensor(666)
x = torch.tensor([2, 2], dtype=float) # 一维张量
print(x) # tensor([2., 2.], dtype=torch.float64)
x = torch.tensor([[2, 3], [4, 5]]) # 二维数组
print(x)
# tensor([[2, 3],
# [4, 5]])
# torch.ones()函数可以生成多维矩阵,矩阵元素初始化全为1
x = torch.ones(2, 3) # 二维数组
print(x)
x = torch.ones(5) #一维
print(x)
x = torch.ones(2, 3, 4) # 三维
print(x)
# tensor([[1., 1., 1.],
# [1., 1., 1.]])
# tensor([1., 1., 1., 1., 1.])
# 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.]]])
# torch.empty() 创建空矩阵,内容未进行初始化,使用时要进行数据初始化
x = torch.empty(2, 3) # 2*3矩阵
print(x)
# tensor([[1.6209e-19, 1.8319e+25, 5.9423e-02],
# [7.0374e+22, 1.6907e-01, 1.7743e+28]])
#torch.zeros() 与torch.ones()类似,区别是内容初始化为0
x = torch.zeros(1, 3, 3, dtype=torch.long) # 创建1*3*3的三维向量
print(x)
# tensor([[[0, 0, 0],
# [0, 0, 0],
# [0, 0, 0]]])
x1 = torch.rand(2, 4) # 2*4的随机数(0--1随机)
print(x)
x2 = torch.randn(2, 5)# 2*5符合正态分布的随机数
print(x)
# tensor([[0.8069, 0.9762, 0.6335, 0.5133],
# [0.4084, 0.9876, 0.6013, 0.4617]])
# tensor([[-1.4031, 0.2296, 0.6661, 0.6075, -0.0034],
# [-0.1969, 1.7592, -0.1239, -1.6503, -0.9498]])
# 利用原有的tensor创建新tensor,可以利用dtype,device,size等属性信息
x = torch.zeros(2, 3, dtype=int)
print("x:{}".format(x))
y = x.new_ones(2, 3)
print("y:{}".format(y))
# x:tensor([[0, 0, 0],
# [0, 0, 0]])
# y:tensor([[1, 1, 1],
# [1, 1, 1]])
# 利用之前tensor的大小,重新定义dtype
z = torch.randn_like(x, dtype=torch.float)
print("z:{}".format(z))
# z:tensor([[-0.8123, 0.0407, 0.4976],
# [1.7102, 2.0772, 0.4945]])
2.定义操作
凡是用Tensor进行各种运算的,都是Function
最终,还是需要用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 等
import torch
s = torch.tensor([[1, 3, 5, 7], [2, 4, 6, 8]])
print(s.size(0), s.size(1), s.size(), sep="---")
# 2---4---torch.Size([2, 4])
print(s.numel()) # s中元素个数
# 8
print(s[0][2]) # s的第0行第2列元素(从0开始)
# tensor(5)
print(s[:, 1]) # s第一列所有元素
# tensor([3, 4])
print(s[1, :]) # s第一行所有元素
# tensor([2, 4, 6, 8])
t = torch.arange(1, 5) # 生成2从1——5不包括5公差为1的序列
print(t)
# tensor([1, 2, 3, 4])
print(s @ t) # 两矩阵叉乘
# tensor([50, 60])
print(s[0, :] * t) # *表示对应两两相乘
# tensor([ 1, 6, 15, 28])
print(s[0, :] @ t) # @表示矩阵叉乘
# tensor(50)
print(s + torch.rand(2, 4)) # s矩阵加上一个2*4随机矩阵
# tensor([[1.5769, 3.6351, 5.5506, 7.0818],
# [2.9022, 4.8702, 6.7515, 8.1953]])
print(s.t()) # 矩阵转置
# tensor([[1, 2],
# [3, 4],
# [5, 6],
# [7, 8]])
print(s.transpose(0, 1)) # transpose函数内参数为带转置的两个维度
# tensor([[1, 2],
# [3, 4],
# [5, 6],
# [7, 8]])
# 从3到8生成20个符合等差数列的数 start:3,end:8,step:20
r = torch.linspace(3, 8, 20)
print(r)
# 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])
# 创建两个1*4的tensor
a = torch.tensor([[1, 2, 3, 4]])
b = torch.tensor([[4, 3, 2, 1]])
print(torch.cat((a, b), 0)) # 从第0维上拼接
# tensor([[1, 2, 3, 4],
# [4, 3, 2, 1]])
print(torch.cat((a, b), 1)) # 从第1维上拼接
# tensor([[1, 2, 3, 4, 4, 3, 2, 1]])
from matplotlib import pyplot as plt
# matlabplotlib 只能显示numpy类型的数据,下面展示了转换数据类型,然后显示
# 注意 randn 是生成均值为 0, 方差为 1 的随机数
# 下面是生成 1000 个随机数,并按照 100 个 bin 统计直方图
plt.hist(torch.randn(1000).numpy(), 100, color='k')
plt.show()
# 当数据非常非常多的时候,正态分布会体现的非常明显
plt.hist(torch.randn(10**6).numpy(), 100,color='k')
plt.show()
二.螺旋曲线分类
引入基本的库,然后定义参数
初始化 X 和 Y。 X 可以理解为特征矩阵,Y可以理解为样本标签。 结合代码可以看到,X的为一个 NxC 行, D 列的矩阵。C 类样本,每类样本是 N个,所以是 N*C 行。每个样本的特征维度是2,所以是 2列。
在 python 中,调用 zeros 类似的函数,第一个参数是 y方向的,即矩阵的行;第二个参数是 x方向的,即矩阵的列,大家得注意下,不要搞反了。下面结合代码看看 3000个样本的特征是如何初始化的。
X = torch.zeros(N * C, D).to(device)
Y = torch.zeros(N * C, dtype=torch.long).to(device)
for c in range(C):#C与c不同
index = 0
t = torch.linspace(0, 1, N) # 在[0,1]间均匀的取1000个数,赋给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())
显示图像
2.2构建线性模型分类
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()
精确度只有50%左右
print(y_pred.shape)
print(y_pred[10, :])
print(score[10])
print(predicted[10])
# Plot trained model
print(model)
plot_model(X, Y, model)
2.3两层神经网络分类
ReLu函数作为激活函数
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作为激活函数,精确度达到了95.8%
如果使用sigmoid()作为激活函数
model = nn.Sequential(
nn.Linear(D, H),
nn.Sigmoid(),
nn.Linear(H, C)
)
model.to(device)
可以看出,精确度与ReLu函数相比,Sigmoid函数精确度只有50.3%
LeakyRelu()作为激活函数
model = nn.Sequential(
nn.Linear(D, H),
nn.LeakyReLU(),
nn.Linear(H, C)
)
model.to(device)
tanh()作为激活函数
model = nn.Sequential(
nn.Linear(D, H),
nn.Tanh(),
nn.Linear(H, C)
)
model.to(device)
Tanh激活函数的精确度为83.6%
增加隐层使用两层sigmoid()函数
model = nn.Sequential(
nn.Linear(D, H),
nn.Sigmoid(),
nn.Linear(H, 60),
nn.Sigmoid(),
nn.Linear(60, C)
)
增加隐层多用一次Sigmoid函数的精确度为97.0%
不同的激活函数对比:
Loss | Accuracy | |
无激活函数 | 0.869014 | 0.494 |
ReLu | 0.161963 | 0.958 |
Simoid | 0.763233 | 0.503 |
LeakyReLu | 0.181599 | 0.944 |
Tanh | 0.317361 | 0.836 |
两层Sigmoid | 0.131681 | 0.970 |
小结
Sigmoid()函数作为激活函数如果只嵌套一层相比于ReLu等其他的激活函数则其精确度较弱,但如果增加隐层再次使用Sigmoid函数,那么其精确度会大幅度上升,不同的激活函数会有不同的精准度,使用时应该多尝试,哪个效果好用哪个。