1. 提取特征 2. 分类 提取特征,层 在CV领域,提取特征的一种方法是 卷积 在NLP领域,提取特征的一种方法是 循环神经网络 注意力也能提取特征,通用于CV,NLP 分类: 全连接 中间会穿插一系列增加模型拟合能力的方法: BN MAXPOOL RULE DROPOUT
call方法
python类的init方法可以在调用类生成对象时传递参数
call方法可以为对象传递参数
import numpy as np
class MyModel(object):
def __init__(self, in_feature,out_feature) -> None:
self.in_feature = in_feature
self.out_feature = out_feature
def __call__(self, X) :
x = np.array(X)
return x.mean()
# 模板定义,经过参数初始化,将一个通用模板类转化一个具有特定业务含义的模板
# 就是定义一个参数网络
model = MyModel(in_feature=2,out_feature=1)
X = np.array([1,2,3])
# 让数据流过参数网络
y = model(X)
print(y) # 2.0
深度学习的模型结构
继承nn.Module类,调用super().__init__()完成父类初始化
forward方法实现类似call的功能
总之,深度学习的模型定义通常只有两部分:
1. 定义一个参数网络(言外之意:不含数据)
2. 调用网络:让数据流过参数网络,通过重写forward方法实现
import torch
from torch import nn
import numpy as np
class MyModel(nn.Module):
def __init__(self, in_feature,out_feature) -> None:
super().__init__()
self.in_feature = in_feature
self.out_feature = out_feature
def forward(self, X) :
x = np.array(X)
return x.mean()
# 模板定义,经过参数初始化,将一个通用模板类转化一个具有特定业务含义的模板
# 就是定义一个参数网络
model = MyModel(in_feature=2,out_feature=1)
X = np.array([1,2,3])
# 让数据流过参数网络
y = model(X)
print(y) # 2.0
回归问题:预测得到一个数
import torch
from torch import nn
import torch.nn.functional as F
# torch 批次处理
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
# 因为加入了1%的噪声,所以,不管怎么训练,模型的精度上限是 99%,会有稍许浮动,但不会达到100%
# 这也说明影响精度的两个重要因素:一是模型算法不够好,二是数据噪声太多(这个决定了上限)
X,y = make_regression(n_samples=10000,n_features=100,noise=0.01,random_state=73)
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.1,random_state=73)
class MyDataSet(Dataset):
def __init__(self,X,y):
"""
构建数据集
"""
self.X = X
self.y = y.reshape(-1,1) # 将标签转为2维,与模型输出维度一致
print(f"seq_len={len(X[0])}")
def __len__(self):
return len(self.X)
def __getitem__(self, idx):
x = self.X[idx]
y = self.y[idx]
return torch.tensor(data=x).float(), torch.tensor(data=y).float()
# 自定义数据加载器
train_dataset = MyDataSet(X=X_train,y=y_train)
test_dataset = MyDataSet(X=X_test,y=y_test)
# -----------------------------
# 模型定义
# -----------------------------
class LinearModel(nn.Module):
def __init__(self, in_features=100, out_features=1):
"""初始化参数网络
两层网络相比一层网络,加速了模型收敛的速度
"""
super().__init__()
# 全连接网络1
self.linear1 = nn.Linear(in_features=in_features, out_features=8)
# 最后一层全连接,将数据维度映射到业务需要的维度,
# 因为是回归问题,得到的是一个数
# 这在神经网络中用1维表示,
self.linear2 = nn.Linear(in_features=8, out_features=out_features)
def forward(self, X):
"""让数据流过参数网络"""
x = self.linear1(X)
# 加了一层激活函数,结果验证它可以提升模型的表达能力
# 激活函数没有参数,或者说它的参数是固定的,也没有梯度,不参与梯度运算
x = F.relu(x)
x = self.linear2(x)
return x
model = LinearModel(in_features=100, out_features=1)
# 回归问题常用损失函数MSE
loss_fn = nn.MSELoss()
# 自适应优化法
optim = torch.optim.Adam(params=model.parameters(), lr=1e-3)
# 从数据集中批次取数据
train_dataloader = DataLoader(dataset=train_dataset, shuffle=True, batch_size = 128)
# 训练一行数据,测试代码是否有误
for X,y in train_dataloader:
print(X.shape,X.ndim,y.shape,y.ndim) # torch.Size([128, 100]) 2 torch.Size([128, 1]) 2
y_out = model(X)
# print(y_out.shape,y_out.ndim) # torch.Size([128, 1]) 2
if y_out.ndim != y.ndim:
print('模型输出数据维度与标签维度不一致,无法进行损失计算...')
break
loss = loss_fn(y_out,y)
optim.zero_grad()
loss.backward()
optim.step() # 完成一次模型参数更新,w = w - x.grad
break
# ------------------
# 重要部分到这就结束,总结一下:
# 参数网络定义(模型定义),如何让参数逼近标签(损失函数设计及优化方法)
# ------------------
from ai.dl import T
from ai.params import DATA_ROOT
import os
model_param_path = os.path.join(DATA_ROOT,'linear_model/model2_params2.h5')
log_file = os.path.join(os.path.dirname(os.path.abspath(__file__)),"train.log")
# 自定义训练器,重要不在这里,所以就封装一下
T.train(model=model,loss_fn=loss_fn,optimizer="adam",
train_dataset=train_dataset,
train_dataloader=train_dataloader,
epochs=10,
learning_rate=1e-3,
model_param_path=model_param_path,
test_dataset=test_dataset,
auto_save=True,
continuation=True,
is_regression=True,
log_file=log_file)
回归模型一次输出一个数,比如[3.14] DL模型都是批次计算,一个批次如下 [ [3.14] [3.15] [3.16] ] 所以模型的输出是个2维矩阵 第2维只有一个数,其结果就是我们需要的数值
回归问题的损失函数通常使用MSE
分类问题标签设计
分类模型一次输出n个数,n=3,就是3分类问题,比如[0.1, 0.3, 0.7] DL模型都是批次计算,一个批次如下 [ [0.1, 0.3, 0.7] [0.1, 0.3, 0.7] [0.1, 0.3, 0.7] ] 所以模型的输出是个2维矩阵 第2维的3个数对应着3个类别, 我们的标签是这样的 [ [0, 0, 1] [0, 0, 1] [0, 0, 1] ] 随着模型参数不断逼近标签, 模型输出的第2维数据只有一个元素会逼近1(因为标签是1) 模型输出的第2维数据 其他元素会逼近0(因为标签是0) 于是,模型输出是哪个类型我们就清楚了 也就是说,你怎么设计标签很重要, 遇到其他的分类问题,修改标签的表示方法就能解决不同的问题
分类问题损失计算
明白了模型输出与真实标签的shape后,那怎么计算它们之间的损失? 这实际上是要计算两个分布之间的距离 分解之后就是如何计算两个向量之间的距离, 接上面的例子,就是要计算 模型输出向量 [0.1, 0.3, 0.7] 与能表示标签的向量[0, 0, 1] 之间的距离 这就要引入 交叉熵 的概念 另外要注意,不管是分类还是回归,在深度学习都是批量计算,损失值最后求的是平均值
将pytorch模型保存为onnx
仅将pytorch模型保存为onnx,并不加载运行,只是为其他程序提供onnx格式的模型,
这时仅安装pytorch包就可以
import torch
import torchvision
model = torchvision.models.resnet18()
model.eval()
#[B,C,H,W],trace需要通过实际运行一遍模型导出其静态图,故需要一个输入数据
h0 = torch.zeros(1, 3, 32, 32)
# trace方式,在模型设计时不要用for循环,
# 能在模型外完成的数据操作不要在模型中写,
# 不用inplace等高大上的语法,保持简单,简洁,否则onnx可能无法完全转换过去
torch.onnx.export(
model=model,
# model的参数,就是原来y_out = model(args)的args在这里指定了
# 有其shape能让模型运行一次就行,不需要真实数据
args=(h0,),
# 储存的文件路径
f="model02.onnx",
# 导出模型参数,默认为True
export_params = True,
# eval推理模式,dropout,BatchNorm等超参数固定或不生效
training=torch.onnx.TrainingMode.EVAL,
# 打印详细信息
verbose=True,
# 为输入和输出节点指定名称,方便后面查看或者操作
input_names=["input1"],
output_names=["output1"],
# 这里的opset,指各类算子以何种方式导出,对应于symbolic_opset11
opset_version=11,
# batch维度是动态的,其他的避免动态
dynamic_axes={
"input1": {0: "batch"},
"output1": {0: "batch"},
}
)
加载onnx
import onnx
model_onnx = onnx.load("model02.onnx") # 加载onnx模型
onnx.checker.check_model(model_onnx) # 验证onnx模型是否加载成功
调用onnx
import onnxruntime
import numpy as np
# 创建会话
session = onnxruntime.InferenceSession("model02.onnx",providers=[ 'CPUExecutionProvider'])
# session = onnxruntime.InferenceSession("model02.onnx",providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
x = np.random.randn(1, 3, 32, 32)
ort_input = {session.get_inputs()[0].name: x.astype(np.float32)}
ort_output = session.run(None, ort_input)
示例1
#[B,C,H,W],trace需要通过实际运行一遍模型导出其静态图,故需要一个输入数据
h0 = torch.zeros(1, 1350)
# trace方式,在模型设计时不要用for循环,
# 能在模型外完成的数据操作不要在模型中写,
# 不用inplace等高大上的语法,保持简单,简洁,否则onnx可能无法完全转换过去
torch.onnx.export(
model=model,
# model的参数,就是原来y_out = model(args)的args在这里指定了
# 有其shape能让模型运行一次就行,不需要真实数据
args=(h0,),
# 储存的文件路径
f=f"model/{pm.model_name}_{pm.model_version}.onnx",
# 导出模型参数,默认为True
export_params = True,
# eval推理模式,dropout,BatchNorm等超参数固定或不生效
training=torch.onnx.TrainingMode.EVAL,
# 打印详细信息
verbose=True,
# 为输入和输出节点指定名称,方便后面查看或者操作
input_names=["input"],
output_names=["output"],
# 这里的opset,指各类算子以何种方式导出,对应于symbolic_opset11
opset_version=11,
# batch维度是动态的,其他的避免动态
dynamic_axes={
"input": {0: "batch"},
"output": {0: "batch"},
}
)
import onnxruntime
import numpy as np
# 创建会话
session = onnxruntime.InferenceSession(f"model/{pm.model_name}_{pm.model_version}.onnx",providers=[ 'CPUExecutionProvider'])
# session = onnxruntime.InferenceSession("model02.onnx",providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
x = np.random.randn(1, 1350)
ort_input = {session.get_inputs()[0].name: x.astype(np.float32)}
ort_output = session.run(None, ort_input)
ort_output
[array([0.48087886, 0.51912105], dtype=float32)]
session.get_inputs()[0].name
'input'
第一步:大方向
面临的业务问题是什么? 总体上,有哪些模型可供参考 要使用哪些/哪类思想? 要解决什么问题?
第二步:数据流转过程
数据是如何从业务转到模型的? 模型的输入,输出是什么 ? 标签的shape逆向决定了模型输出的shape 损失函数的目标是什么? 如何设计损失函数,才会让 当损失函数越来越小时,模型输出逼近目标 ? 有没有现成的可供调用,还是需要自己手工设计? 数据从业务开始,最后又返回业务,得到一个业务能看明白的结果, 这是个一体化的过程, 比如前面进行的很顺利,但损失函数找不到现成的,自己又设计不出来合适的, 前面做那么多工作又有什么用!!! 数据到模型,模型设计,损失函数设计,训练设计,预测设计,返回业务等任何一个环节搞不定, 整体就不会有好结果! 只有将整个过程弄个七七八八,才可以开始这个工程...
第三步:具体实现
就是将第二步思考的内容实现出来, 先定下一个baseline, 然后对细节进行优化,做了什么使精度或速度提升了多少... 再加上日志, 考虑灰度上线, 如何对外提供服务, 如何架框转换可以提速, 日后运行维护的工程化问题等等
第四步:看看别人如何做的
前三步可能是你一个人在做, 做完后,如果有机会看看别人怎么处理类似的问题 若只顾着自己,那不就是“闭门造车”嘛 向外看看,总会有些收获 AI的算法思想很多本身就是参考其他领域解决问题的方法来的... 高级算法工程师 都是自己设计网络, 现成的API内部如何实现的都是明白的, 没有现成可供调用时,自己是有能力设计一个的
|
深度学习中计算机干了什么 计算机在暴力计算参数,暴力求导,根据你的目标(损失函数要求的方向),计算机在进行全遍历计算, 换句话,计算机依指令行事... 你让它干什么它就干什么 深度学习中算法工程师在干什么 算法工程师在设计网络, 算法工程师面前有一个问题,比如,图片一个像素有三色,每色0-255, 多个像素组合出来的视角信息太多了, 上面有个花, 现在你想单把朵花取下来放到一张新图片上, 于是算法工程师发话了, 将这三色 “微分” 一下,拆分成1000个不同的成份,每份都是图像的一部分, 怎么分呢,计算机开始随机就给它拆分成了1000份, 如此做法,猜想一下,这朵花大概...大概会在这1000某一些部分上, 这里就假设这朵花在这1000张新图像上的其中100张上吧 于是算法工程师又发话了,将这1000转成100, 计算机对这1000张图像进行计算,融合, 主要就是看看每张图片上哪个像素重要,哪个不重要, 这样的过程重复了100次,每次得到一个新的维度,100次得到了100维 算法工程师指定了参数的网络结构, 计算机让数据流过所有的网络, 根据损失函数梯度下降反馈的结果不断在调整着网络上的参数, 某一层网络的某个区域重要,就将这个区域的参数升一升, 这一层网络作用不大,就将参数一降再降,然后归于0, 那一层网络作用很大,就将参数一升再降,达到某个值 整个网络上的参数在不断变大变小,不断变换,最后趋于稳定 深度学习竞争点 作为算法工程师:比谁设计的网络结构好 作为厂家:比谁家的算力强,谁的硬件让客户,让算法工程师用着舒服
|
梯度下降最伟大的地方在于它 实现了自动化,并且有一定的智能,
只要算力够,它大概率会朝着一个局部最优的方向前进
在这个大前提下,讨论的范围缩小到,分析影响它靠近目标效率的因素有哪些
数据的质量,一个批次的数据代表全体数据集,
数据质量好,可以直指目标的大幅度梯度,可以快速接近目标
- 如果数据质量差,脏数据多,总是受到各种不良诱导不断偏离目标,这效率就低
模型算法的结构,工欲善其事,必先利其器;
这器就是模型,骑自行车行百里和坐车效率真的不一样
|
|
|
|
|
|
|
深度学习目前处于高速发展阶段,各种论文,甚至构架都在不断新生 重在学习论文的思想,解决问题的主方法, 细节与实现的技巧,参考一下就可以了