总结/扩展/其他
人工智能包含机器学习,机器学习包含深度学习
人工智能的应用主要包括以下这几项:
- 专家系统
- 自然语言处理
- 计算机视觉
- 语音系统
- 机器人
- 其他
其中自然语言处理、计算机视觉与语音系统是现在大热的几个方向
大多数我们可以体验到的深度学习应用,都属于有监督学习,例如人脸识别、图像分类、手势识别、人像分割、情感分析等。而最近几年特别流行的GAN就属于无监督学习
CNN(Convolutional Neural Networks):卷积神经网络
RNN(Recurrent Neural Network):循环神经网络
DNN(Deep Neural Networks):深度神经网络
GNN(Graph Neural Networks):图神经网络
BRNN(Recurrent Neural Network):双向长短时记忆循环神经网络
GAN(Generative Adversarial Networks):生成式对抗网络
NLP(Natural Language Processing):自然语言处理
MTL(Multi-Task-Learning):多任务学习
LSTM(Long Short-Term Memory):长短期记忆网络
CV(Computer Vision):计算机视觉
SVM(Support Vector Machine,):支持向量机
KNN(K-NearestNeighbor):K最近邻分类算法
NBM(Naive Bayesian Model):朴素贝叶斯模型
GAN
GAN,即生成对抗网络(Generative Adversarial Network),是一种深度学习模型
GAN的核心概念涉及两个关键的组成部分:生成器(Generator)和判别器(Discriminator)。这两个部分在训练过程中相互竞争,从而提升彼此的性能。具体来说:
- 生成器:它的目标是创建足够真实的数据,以至于判别器无法区分这些数据是真实的还是由生成器产生的
- 判别器:它的目标是正确识别输入数据是真实的还是由生成器生成的假数据
- 值得一提的是,GAN的应用非常广泛,包括但不限于图像生成、图像到图像的转换(如风格迁移)、图像超分辨率、图像修复、数据增强等。此外,GAN在艺术创作、游戏开发、药物设计等领域也有应用
总的来说,GAN作为一种深度学习技术,通过模拟两个网络之间的对抗过程,能够生成高质量的数据,对于推动人工智能的发展具有重要意义
机器学习的几个步骤
- 数据处理:主要包括数据清理、数据预处理、数据增强等。总之,就是构建让模型使用的训练集与测试集
- 模型训练:确定网络结构,确定损失函数与设置优化方法
- 模型评估:使用各种评估指标来评估模型的好坏
有些场景,客户一般不会提供测试集(防止你作弊,把测试数据也拿去训练),所以可以细分为3类:训练集、验证集、测试集
如果只提供了训练集(没有提供验证集的话),我们可以自己在训练集上面拆出一小部分作为验证集
方式可以为:每次都随机一小部分、或者不随机取1/N来进行交叉验证等
train_data,val_data = torch.utils.data.random_split(train_data, [50000, 10000])
模型训练的几个步骤
- 模型结构设计:例如,机器学习中回归算法、SVM等,深度学习中的VGG、ResNet、SENet等各种网络结构,再或者你自己设计的自定义模型结构
- 给定损失函数:损失函数衡量的是当前模型预测结果与真实标签之间的差距
- 给定优化方法:与损失函数搭配,更新模型中的参数
神经网络基础层
隐藏层下的层未必只属于隐藏层,不用太拘泥于这种"范式"
- 输入层(Input Layer): 接受原始输入数据的层
- 隐藏层(Hidden Layer): 可以有多个隐藏层,用于学习数据的抽象表示。包含线性层(全连接层)、激活层、卷积层、池化层等
- 线性层(全连接层): 在隐藏层中,线性层用于执行矩阵乘法操作,将输入数据与权重相乘,并添加偏置项。这有助于学习输入数据的复杂关系和模式
- 激活层 : 当值达到某一个阈值的时候才激活。激活函数在线性层的输出上引入非线性变换。常见的激活函数包括ReLU(Rectified Linear Unit)、Sigmoid、Tanh等。激活函数有助于神经网络学习非线性映射,增强网络的表示能力
- 批归一化层(Batch Normalization Layer): 在训练过程中对每个批次的输入进行归一化,有助于加速训练过程、模型的稳定性。它对于缓解梯度消失和爆炸问题也有一定的效果
- 正则化层(Regularization Layer): 包括丢弃层(Dropout Layer)丢弃层在训练过程中随机丢弃一部分神经元、权重正则化层(L1、L2)等,以减小过拟合的风险
- 输出层(Output Layer): 提供最终网络的输出。输出层的激活函数取决于任务类型,如分类任务可能使用Softmax(很适合把数据压缩为概率0-1区间,累加为1),回归任务可能使用线性激活函数
- 损失层(Loss Layer): 计算模型预测与实际标签之间的差异,产生损失。通常与任务类型相关,如交叉熵损失用于分类任务,均方误差用于回归任务
- 优化器层(Optimizer Layer): 使用反向传播算法更新网络参数以最小化损失。包括梯度下降、Adam、RMSProp等优化算法
- 自适应学习率调整层(Learning Rate Scheduler Layer): 根据训练过程中的性能动态调整学习率,有助于更稳定的收敛
- 自动编码器层(Autoencoder Layer): 如果在训练中使用自动编码器进行无监督学习或特征学习,这些层也会包括编码器和解码器
权重共享
from torch import nn
class My_Model(nn.Module):
def __init__(self):
super(My_Model, self).__init__()
# 参数绑定,权重共享
shared = nn.Conv2d(3, 32, 5, 1, 2)
self.model = nn.Sequential(
shared,
nn.MaxPool2d(2),
shared,
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64 * 4 * 4, 64),
nn.Linear(64, 10)
)
def forward(self, x):
return self.model(x)
线性模型与模型参数
import torch
from torch import nn
class LinearModel(nn.Module):
def __init__(self):
super().__init__()
# nn.Parameter(Tensor),将Tensor加到模型中,便于被优化器优化,会自动requires_grad=True,不用显示在Tensor上指定了
self.weight = nn.Parameter(torch.randn(1))
self.bias = nn.Parameter(torch.randn(1))
def forward(self, input):
return (input * self.weight) + self.bias
对于一个模型的可训练的参数,我们可以通过named_parameters()来查看
'''
('weight', Parameter containing: tensor([2.0071], requires_grad=True))
('bias', Parameter containing: tensor([3.1690], requires_grad=True))
'''
for parameter in model.named_parameters():
print(parameter)
可以使用有序字典来访问
model.state_dict()
model[2].state_dict()
手动实现一个线性优化
import numpy as np
# 损失函数,平方损失,loss = (y - (wx + b))^2
def loss_fn(w, b, points):
total_error = 0
l = len(points)
for i in range(0, l):
x = points[i, 0]
y = points[i, 1]
total_error += (y - (w * x + b)) ** 2
return total_error / float(l)
# 优化函数,梯队下降,对损失函数的w、b分别求导,然后叠加学习率的影响
def grad_step(w, b, points, lr):
b_grad = 0
w_grad = 0
l = len(points)
n = float(l)
for i in range(l):
x = points[i, 0]
y = points[i, 1]
b_grad += -(2 / n) * (y - (w * x + b))
w_grad += -(2 / n) * x * (y - (w * x + b))
new_w = w - (lr * w_grad)
new_b = b - (lr * b_grad)
return new_w, new_b
# 初始化w、b
w = 0
b = 0
# 随机初始化100个点
points = np.random.normal(30, 2, (100, 2))
# 学习率
lr = 0.001
# 训练100轮
for i in range(100):
loss = loss_fn(w, b, points)
print(f"w:{w},b:{b},loss:{loss}")
# 进行一轮训练,优化w、b参数
w, b = grad_step(w, b, points, lr)
常见参数
- Torchvision中推荐的图片最小训练尺寸是:224 × 224
- Torchvision中所有"图像分类"的预训练模型都是在ImageNet上训练的,我们需要使用均值为[0.485, 0.456, 0.406],标准差为[0.229, 0.224, 0.225]对数据进行正规化
transform = transforms.Compose([
transforms.RandomResizedCrop((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
其他常用数据集的均值和标准差
transforms.Normalize((0.1307,), (0.3081,)) # mnist的均值和标准差 训练集
transforms.Normalize((0.1326,), (0.3106,)) # mnist的均值和标准差 测试集
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)) # CIFAR-10训练集
transforms.Normalize((0.4940, 0.4850, 0.4504), (0.2467, 0.2429, 0.2616)) # CIFAR-10测试集
常见优化
包括性能优化、最小值优化(梯度下降被困在鞍点或局部最小值或振荡不下降)等
用 Hessian Matrix 可以判断是local min、local max、saddle point,但是性能不好,实际场景不用
- gradient norm < 0.001 and minimum ratio > 0.5 => local minima like
- gradient norm < 0.001 and minimum ratio <= 0.5 => saddle point
- gradient norm > 0.001 => none of the above
调整初始值:尝试不同的初始值,或使用一些常用的权重初始化方法。它们的目标都是为了解决权重初始化不当可能导致的问题,如训练不稳定、收敛速度慢等
def my_init(m):
if type(m) == nn.Linear:
# 初始化权重,正态分布,-10到10
torch.nn.init.normal_(m.weight, -10, 10)
# 初始化偏置,全为0
nn.init.zeros_(m.bias)
# 权重可以调整,不止是初始化的场景
m.weight.data *= m.weight.data.abs() >= 5
net.apply(my_init)
Kaiming初始化(也称为He初始化)由何恺明提出,主要针对使用ReLU或其变种激活函数的深度神经网络设计。它考虑了网络层之间的方差传递问题,通过调整权重矩阵的尺度来保持每一层输入信号的方差,从而避免梯度消失或爆炸的问题
Kaiming初始化在Pytorch中被用作默认的参数初始化函数,尤其适用于较深的网络结构。torch.nn.init.kaiming_normal_(m.weight)
等
Xavier初始化:又称为Glorot初始化,它是为使用sigmoid或tanh等饱和性激活函数的神经网络设计的。Xavier初始化的目标是保持前向和后向传播过程中信号的方差一致,从而使得网络的学习更加稳定
这种初始化方法适用于具有sigmoid或tanh激活函数的神经网络。torch.nn.init.xavier_normal_(m.weight)
等
调整激活函数:relu、tanh在0到1之间较稳定,sigmoid在0到1之间不那边稳定,可以调整为:4 * sigmoid(x) - 2使其稳定
通常使用relu就行了,因为它简单又稳定。但是如果有些场景relu效果不好,可以尝试其他的激活函数看看
调整学习率:尝试使用不同的学习率进行训练,也可以使用学习率衰减的方式
- LOSS到达阈值轮不下降的时候lr下降,scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(torch.optim.optimizer, 'min'),scheduler.step(loss)
- 每30轮下降10%的lr,scheduler = torch.optim.lr_scheduler.StepLR(torch.optim.optimizer,step_size=30,gamma=0.1),scheduler.step(loss)
- RMSProp,Adam=RMSProp + Momentum
- Warm Up,RAdam
- 平坦的路上学习率可以适当增大,陡峭的路上学习率可以适当减少
调整动量(惯性):防止在局部最优点出不来,增加一点惯性。梯度下降的时候结合上一次的梯度方向(防止冲到坑里出不来)。但是也有可能导致梯度下降过慢
使用正则化技术:为了防止过拟合,可以在损失函数中添加正则化项,如L1、L2正则化、dropout正则化等。有些场景,比如房价(更关心相对误差),可以使用:
# weight_decay:权重衰减(正则化),0.01表示接近于0,使得模型复杂度降低,过拟合场景使用
# momentum:动量、惯性,0.7表示上一次梯度权重0.7,当前梯度权重0.3
optim.SGD(net.parameters(),lr=0.01, weight_decay=0.01, momentum=0.7)
# forward的时候随机断掉一些权重,减少参数,简化网络
torch.nn.Dropout(0.4) # 随机断掉40%的连接
数据增强:对训练数据进行更多的变换,如旋转、缩放、翻转等,以提高模型的泛化能力
import torchvision
# 随机翻转、裁剪等,可以在不同轮训练时有不同的效果,来达到图片变多的效果(但是效果不会太明显,比实际的样本图片变多要差很多)
train_data = torchvision.datasets.MNIST("../data", train=True, download=True, transform=torchvision.transforms.Compose([
torchvision.transforms.RandomHorizontalFlip(), # 随机水平翻转
torchvision.transforms.RandomVerticalFlip(), # 随机垂直翻转
torchvision.transforms.RandomRotation(15), # 随机旋转15度
torchvision.transforms.RandomCrop([28, 28]), # 随机裁剪
torchvision.transforms.Resize([32, 32]), # 裁剪后还原尺寸
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize((0.1307,), (0.3081,))
]))
使用预训练模型:如果可能的话,可以使用在类似任务上预训练过的模型作为基础,然后在此基础上进行微调。这通常可以提高模型的性能
修改网络结构:尝试使用更复杂(欠拟合)或更简单(过拟合)的网络结构
损失函数的定义,比如:分类问题用交叉熵比最小二乘法更容易下降loss
批量归一化(Batch Normalization):作用在全连接层和卷积层输入/输出上,激活函数前
- 固定小批量中的均值和方差,然后学习出适合的偏移和缩放
- 对全连接层,作用在特征维;对于卷积层,作用在通道维
- 最初论文是想用它来减少内部协变量转移,后续有论文指出它可能就是通过在每个小批量里面加入噪音来控制模型复杂度,因此没必要跟丢弃法混合使用
- 在线上应用预测(测试集)时,可能不能等到一个batch_size再进行,需要拿训练集上算好的参数
- 可以加速收敛速度,但一般不改变模型精度
残差连接:主要作用是解决深度神经网络训练过程中的退化问题
早停法:根据验证集正确率和loss下降等条件,提前停止训练。这可以防止模型在训练集上过拟合
随机梯度下降:在所有数据集上的梯度均值变为一个batch上的梯度均值。由于显存限制,数据量大的话也不可能求所有数据的均值
提供更多的训练数据、调整batch_size(小的batch_size有更多的噪音,但是结果可能反而更好。batch_size太大容易使模型收敛在局部最优点),具体要根据数据量的大小和显存来权衡
增加训练轮数:将训练轮数从100增加到更大的值,例如200或300,以便模型有更多的机会学习数据的特征
在训练集效果很好,测试集效果很差,可能是过拟合,也有可能是测试集和训练集的分布完全不相干
Google Colab
# 执行Python
import pytorch
# 执行Linux Shell
!ls -l
# 调用notebook魔术方法
%ls -l
ASIC
大公司都在造自己的专用芯片,因为通用芯片(目前主要是英伟达)太贵了且容易被卡脖子
Google TPU是标志性的芯片,能够媲美Nvidia GPU的性能,核心是systolic array
专用芯片的好处是:芯片开发周期、成本相对较低,在特定场景能做到性能大于通用芯片(比如:矩阵运算等)
缺点是:太专用了,一旦某个领域,如人工智能突然不火了,那么为其量身定制的芯片可能就没有用了
而且人工智能有很多不同的层,且在不断的发展,可能需要针对不同的层或不断的变化发展,来调整相应的专用芯片
流片
流片:是指集成电路制造过程中,将设计好的电路图案转移到硅片上的步骤,也称为芯片制造或芯片生产。这个过程涉及到光刻、蚀刻、离子注入等多个复杂的工艺步骤,最终形成能够实现特定功能的集成电路芯片。流片是一个成本较高且时间周期较长的过程,通常用于ASIC(Application-Specific Integrated Circuit,应用特定集成电路)的生产
FPGA
FPGA(Field-Programmable Gate Array):是一种可编程的逻辑器件,可以通过编程来配置其内部的逻辑门和电路连接,实现特定的数字逻辑功能。它允许设计师在不改变硬件的情况下,通过修改配置文件来改变电路的功能。FPGA通常用于原型设计、数字信号处理、硬件仿真等领域
烧芯片
烧芯片:指的是将编写好的程序或数据永久地写入到微控制器或其他类型的非易失性存储器芯片中的过程。这个步骤通常在芯片的最后测试阶段进行,一旦完成,芯片就可以按照设计的功能正常工作了