概念:人工智能是使一部机器像人一样进行感知、认知、决策、执行的人工程序或系统。
人工智能的三个层面:计算智能----能存会算;感知智能----能听会说、能看会认;认知智能----能理解,会思考。
最常用定义:计算机系统能够利用经验提高自身的性能。
应用技术领域:计算机视觉、语音技术、自然语言处理。
学习过程:
(1)模型:对要学习问题映射的假设(问题建模,确定假设空间)。
(2)策略:从假设空间中学习/选择最优模型的准则(确定目标函数)。
(3)算法:根据目标函数求解最优模型的具体计算方法(求解模型参数)。
模型分类:
(1)数据标记: 监督学习模型、无监督学习模型
(2)数据分布:参数模型、非参数模型
(3)建模对象:判别模型、生成模型
监督学习的样本具有标记(输出目标),从数据中学习标记分界面(输入-输出的映射函数),适用于预测数据标记。
无监督学习的样本没有标记,从数据中学习模式,适用于描述数据。
参数模型:对数据分布进行假设,待求解的数据模式/映射可以用一组有限且固定数目的参数进行刻画。如线性回归、逻辑回归、感知机、K均值聚类。有些时候数据没有提供足够信息来事先假设分布、或者问题本身没有明显的分布特性。
非参数模型:不对数据分布进行假设,数据的所有统计特性都来源于数据本身。如K近邻模型、SVM、决策树、随机森林。
注:非参≠无参。“参数”指数据分布的参数,而不是模型的参数非参数模型的时空复杂度一般比参数模型大得多。参数模型的模型参数固定,非参数模型是自适应数据的,模型参数随样本变化而变化。
生成模型: 对输入X和输出Y的联合分布P(X, Y)建模,如朴素贝叶斯、隐马尔可夫、马尔科夫随机场。对于监督学习问题,预测标记实际是在求P(Y|X) ,生成模型先从数据中学习联合概率分布P(X, Y);然后利用贝叶斯公式求P(Y|X) 。
判别模型: 对已知输入X条件下输出Y的条件分布P(Y|X) 建模,如SVM、逻辑回归、条件随机场、
决策树。对于监督学习问题,判别模型直接学习P(Y|X),输入特征X,直接预测出最可能的Y。
关系:人工智能>机器学习>深度学习
三个助推剂:大数据、算法、计算力。
深度学习的六个“不能”:
(1)算法输出不稳定,容易被“攻击”。
(2)模型复杂度高,难以纠错和调试。
(3)模型层级复合程度高,参数不透明。
(4)端到端训练方式对数据依赖性强,模型增量性差。
(5)专注直观感知类问题,对开放性推理问题无能为力。
(6)人类知识无法有效引入进行监督,机器偏见难以避免。
解释性:
神经元继续传递信息、产生新连接的概率(超过阈值被激活,但不一定传递),没有激活函数相当于矩阵相乘,多层和一层一样,只能拟合线性函数。
激活函数举例:
(1)线性函数、斜面函数、阈值函数、符号函数。
(2)S性函数:缺点是容易饱和,输出不对称。
(3)双极S性函数:解决了输出不对称问题,但依旧容易饱和。
(4)RELU修正线性单元:上述两个问题都能解决。它的缺点是若输入很大的负值时,永远不会得到正值。
如果一个隐层包含足够多的神经元,三层前馈神经网络(输入-隐层输出)能以任意精度逼近任意预定的连续函数。
双隐层感知器逼近非连续函数:当隐层足够宽时,双隐层感知器(输入-隐层1-隐层2-输出)可以逼近任意非连续函数:可以解决任何复杂的分类问题。
神经网络学习如何利用矩阵的线性变换加激活函数的非线性变换,将原始输入空间投影到线性可分的空间去分类/回归。
增加节点数:增加维度,即增加线性转换能力。
增加层数:增加激活函数的次数,即增加非线性转换次数
在神经元总数相当的情况下,增加网络深度可以比增加宽度带来更强的网络表示能力:产生更多的线性区域。深度和宽度对函数复杂度的贡献是不同的,深度的贡献是指数增长的,而宽度的贡献是线性的。
多层神经网络可看成是一个复合的非线性多元函数 F(⋅): X → Y,给定训练数据{ xi, yi },希望损失 尽可能小
梯度:多元函数f(x, y)在每个点可以有多个方向,每个方向都可以计算导数,称为方向导数。梯度是一个向量,方向是最大方向导数的方向,模为方向导数的最大值。
无约束优化:梯度下降,参数沿负梯度方向更新可以使函数值下降。
为解决局部极小值和梯度消失,可以对权重初始化,选择一个较好的初始值,每次训练一个三层网络,逐层预训练,并进行微调。
假设输出与输入相同(target=input),是一种尽可能复现输入信号的神经网络。将input输入一个encoder编码器,就会得到一个code;加一个decoder解码器,输出信息。通过调整encoder和decoder的参数,使得重构误差最小。
没有额外监督信息: 无标签数据,误差的来源是直接重构后信号与原输入相比得到。
RBM是两层神经网络,包含可见层v(输入层)和隐藏层h。不同层之间全连接,层内无连接 。二分图,与感知器不同,RBM没有显式的重构过程。两个方向权重w共享,偏置不同。
PyTorch主要提供了两个高级功能:GPU加速的张量计算、构建在反向自动求导系统上的深度神经网络。
一般定义数据使用torch.Tensor
import torch
# 可以根据数据直接创建
x = torch.tensor(666)
print(x)
# 可以是一维数组(向量)
x = torch.tensor([1,2,3,4,5,6])
print(x)
# 可以是二维数组(矩阵),返回一个全为1的张量
x = torch.ones(2,3)
print(x)
# 可以是任意维度的数组(张量),形状由可变参数sizes定义
x = torch.ones(2,3,4)
print(x)
# 创建一个空张量
x = torch.empty(5,3) #一个5*3的未初始化的张量
print(x)
# 创建一个随机初始化的张量
#包含了从区间[0, 1)的均匀分布中抽取的一组随机数
x = torch.rand(5,3)
print(x)
# 创建一个全0的张量,里面的数据类型为 long
x = torch.zeros(5,3,dtype=torch.long)
print(x)
# 基于现有的tensor,创建一个新tensor,
# 从而可以利用原有的tensor的dtype,device,size之类的属性信息
y = x.new_ones(5,3) #tensor new_* 方法,利用原来tensor的dtype,device
print(y)
# 利用randn_like方法得到相同张量尺寸的一个新张量,并采用随机初始化来对其赋值
z = torch.randn_like(x, dtype=torch.float) # 利用原来的tensor的大小,但是重新定义了dtype
print(z)
基本运算包括: 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 等。
# 创建一个 2x4 的tensor
m = torch.Tensor([[2, 5, 3, 7],
[4, 2, 1, 9]])
#参数是0,则返回矩阵的行数,参数是1,则返回矩阵的列数
#sep指定每个值以"--"进行一个分隔
print(m.size(0), m.size(1), m.size(), sep=' -- ')
# 返回 m 中元素的数量
print(m.numel())
# 返回 第0行,第2列的数
print(m[0][2])
# 返回 第1列的全部元素
print(m[:, 1])
# 返回 第0行的全部元素
print(m[0, :])
# Create tensor of numbers from 1 to 5
# 注意这里结果是1到4,没有5
v = torch.arange(1, 5)
print(v)
# Scalar product向量点乘
# 1*2+2*5+3*3+4*7=49 4*1+2*2+1*3+9*4=47
m @ v
# Calculated by 1*2 + 2*5 + 3*3 + 4*7
m[[0], :] @ v
#将大小为 2x4 的随机张量添加到 m
m + torch.rand(2, 4)
# 转置,由 2x4 变为 4x2
print(m.t())
# 使用 transpose 也可以达到相同的效果
print(m.transpose(0, 1))
# returns a 1D tensor of steps equally spaced points between start=3, end=8 and steps=20
#等距点 (8-3)/19 = 0.26315
torch.linspace(3, 8, 20)
# 创建两个 1x4 的tensor
a = torch.Tensor([[1, 2, 3, 4]])
b = torch.Tensor([[5, 6, 7, 8]])
# 在 0 方向拼接 (即在 Y 方各上拼接), 会得到 2x4 的矩阵
print( torch.cat((a,b), 0))
# 在 1 方向拼接 (即在 X 方各上拼接), 会得到 1x8 的矩阵
print( torch.cat((a,b), 1))
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);
!wget -no-check-certificate https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py
#引入基本的库
import random
import torch
from torch import nn, optim
import math
from IPython import display
import plot_data, plot_model, set_default
# 因为colab是支持GPU的,torch 将在 GPU 上运行
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('device: ', device)
# 初始化随机数种子。神经网络的参数都是随机初始化的,
# 不同的初始化参数往往会导致不同的结果,当得到比较好的结果时我们通常希望这个结果是可以复现的,
# 因此,在pytorch中,通过设置随机数种子也可以达到这个目的
seed = 12345
random.seed(seed)
torch.manual_seed(seed)
#初始化重要参数
N = 1000 # 每类样本的数量
D = 2 # 每个样本的特征维度
C = 3 # 样本的类别
H = 100 # 神经网络里隐层单元的数量
#3000个样本的特征初始化 X 和 Y
#调用 zeros 函数,第一个参数是 y方向的,即矩阵的行;第二个参数是 x方向的,即矩阵的列
X = torch.zeros(N * C, D).to(device) #X 可以理解为一个 NxC 行, D 列的特征矩阵
Y = torch.zeros(N * C, dtype=torch.long).to(device) #Y可以理解为样本标签
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())
# visualise the data
plot_data(X, Y)
在PyTorch中,计算得到的梯度值会进行累加,为了减小每次的内存消耗,每一次反向传播前,都要把梯度清零。
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()
使用 print(y_pred.shape) 可以看到模型的预测结果为[3000, 3]的矩阵。每个样本的预测结果为3个,保存在 y_pred 的一行里。值最大的一个即为预测该样本属于的类别score, predicted = torch.max(y_pred, 1) 是沿着第二个方向(即X方向)提取最大值。最大的那个值存在 score 中,所在的位置(即第几列的最大)保存在 predicted 中。
print(y_pred.shape)
print(y_pred[10, :])
print(score[10])
print(predicted[10])
使用 print(model) 把模型输出,可以看到有两层:第一层输入为 2(因为特征维度为主2),输出为 100;第二层输入为 100 (上一层的输出),输出为 3(类别数)。
# Plot trained model
print(model)
plot_model(X, Y, model)
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()
# Plot trained model
print(model)
plot_model(X, Y, model)
从上面图示可以看出,线性模型的准确率最高只能达到 50% 左右,对于这样复杂的一个数据分布,线性模型难以实现准确分类。在两层神经网络里加入 ReLU 激活函数以后,分类的准确率得到了显著提高。
由此,我们同样可以得出在两层神经网络里加入其他激活函数以后的准确率。
举例如下:
(1)加入S性函数(sigmoid)
# 这里可以看到,和上面模型不同的是,在两层之间加入了一个 Sigmoid 激活函数
model = nn.Sequential(
nn.Linear(D, H),
nn.Sigmoid(),
nn.Linear(H, C)
)
model.to(device)
(2)双极S性函数(tanh)
# 这里可以看到,和上面模型不同的是,在两层之间加入了一个 Tanh 激活函数
model = nn.Sequential(
nn.Linear(D, H),
nn.Tanh(),
nn.Linear(H, C)
)
model.to(device)
(3)Leaky ReLU 函数
# 这里可以看到,和上面模型不同的是,在两层之间加入了一个 Leaky ReLU 激活函数
model = nn.Sequential(
nn.Linear(D, H),
nn.LeakyReLU(),
nn.Linear(H, C)
)
model.to(device)