目录
一、手动构建模型
1、概念
模型训练中的三个基础概念:
名词 | 定义 |
---|---|
Epoch | 使用训练集的全部数据对模型进行一次完整训练,被称为"一代训练" |
Batch | 使用训练集中的一小部分样本对模型权重进行一次反向传播的参数更新,这一小部分样本被称为"一批数据" |
Iteration | 使用一个Batch数据对模型进行一次参数更新的过程,被称为"一次训练" |
2、模型构建
0.相关库函数
python
import math
import torch
import random
from sklearn.datasets import make_regression
1.构建数据集
python
def build_data():
# 噪声设置
noise = random.randint(1,5)
# 样本数量
sample = 500
# 真实偏置
bias = 0.5
# 划分数据集 coef=True 表示希望函数返回生成数据的真实系数 coef即为真实系数
x,y,coef = make_regression(n_samples=sample,n_feature=4,coef=True,noise=noise,bias=bias,random_state=666)
# 数据转换为张量
x = torch.tensor(x,dtype=torch.float32)
y = torch.tensor(y,dtype=torch.float32)
coef = torch.tensor(coef, dtype=torch.float32)
return x,y,coef,bias
2.加载数据集
将数据集转换为迭代器,以便在训练过程中进行批量处理。
python
def load_data(x,y):
# 配置参数
# 单批次数量
batch_size = 16
# 样本总数量
n_samples = x.shape[0] # len(x)
# 单轮训练的次数&向上取整
n_batches = math.ceil(n_samples/batch_size)
# 数据索引
indices = list(range(n_samples))
# 打乱索引
random.shuffle(indices)
# 循环从单批次中取出数据
for i in range(0,n_batches):
# 取出每批次对应的范围 同时做防越界处理 数量
start = i*batch_size
end = min((i+1)*batch_size, n_samples)
index = indices[start:end]
# 返回对应数据
yield x[index],y[index]
3.初始化参数
随机初始化权重w和设置偏置b。
python
def initialize(n_feature):
# 根据特征数量设置权重
# 随机数种子
torch.manual_seed(666)
# 随机化权重 正态分布
w = torch.randn(n_features,require_grad=True,dtype=torch.float32)
# 手动设置偏置
b = torch.tensor(0.0,require_grad=True,dtype=torch.float32)
return w,b
4.线性回归
设置模型函数,即进行"前向传播"。
python
def regressor(x,w,b):
return torch.matmul(x,w) + b # x@w + b
5.损失函数
设置为均分误差形式,即反向传播的对象。
python
def Loss(y_pred,y_true):
# 均方误差
return torch.mean((y_pred - y_true)**2)
6.优化器
用在梯度更新,向梯度下降的方向更新。
python
def optim_step(w,b,dw,db,lr):
w.data = w.data - lr*dw.data
b.data = b.data - lr*db.data
return w,b
7.训练数据集
python
def train():
# 生成数据
x,y,coef,bias = build_data()
# 初始化参数
w,b = initialize(n_features=x.shape[1])
# 训练参数
lr = 0.01 # 学习率
epoch = 50 # 训练次数
# 训练数据
for i in range(epoch):
total_loss = 0 # 误差总和
count = 0 # 训练次数计数
# 每个批次中的数据
for batch_x,batch_y_true in load_data(x,y):
# 预测数据
batch_y_pred = regressor(batch_x,w,b)
# 计算损失函数
loss = MSE(batch_y_pred,batch_y_true) # 因为是均方误差 所以不需要讲究顺序
total_loss += loss
count += 1
# 梯度清零
if w.grad is not None:
w.data.zero_()
if b.grad is not None:
b.data.zero_()
# 反向传播 计算梯度
loss.backward()
# 梯度更新 得出预测权重和偏置
w,b = optim_step(w,b,w.grad,b.grad,lr)
print(f'epoch:{i},loss:{total_loss/count}')
return w.data,b,coef,bias
8.预测数据
python
def detect(x,w,b):
return torch.matmul(x.type(torch.float32),w) + b # tensor相乘加上偏置
9.调用
python
if __name__ == "__main__":
w,b,coef,bias = train()
print(f'真实系数:{coef},真实偏置:{bias}')
print(f'预测系数:{w},预测偏置:{b}')
# 根据手动添加的特征预测结果 这里添加了两个
y_pred = detect(torch.tensor([[4,5,6,6],[7,8,8,9]]),w,b)
print(f'y_pred:{y_pred}')
二、官方模型定义组件
1、模型组件
0.库函数
python
import torch
import torch.nn as nn
import torch.optim as optim
1.损失函数组件
python
def test01():
"""
损失函数组件
"""
y_true = torch.tensor([1,2,3,4,5,6],dtype=torch.float32)
y_pred = torch.tensor([2,3,4,5,6,7],dtype=torch.float32)
# 均分误差工具
loss = nn.MSELoss()
e = loss(y_true,y_pred)
print(e)
2.线性回归模型组件
python
def test02():
"""
线性回归模型组件
"""
model = nn.Linear(4,1) # w1x1+w2x2+w3x3+w4x4+b=y 隐式操作:w1 w2 w3 w4已经初始化
# x = torch.tensor([[1,2,3,4]],dtype=torch.float32)
# y = model(x)
print(model.parameters())
x1 = torch.tensor([[1,2,3,4],[1,2,3,4],[1,2,3,4]],dtype=torch.float32)
y1 = model(x1)
# print(y)
print(y1)
3.优化器方法
python
def test03():
"""
优化器方法
"""
# 梯度更新
# 01 构造数据集
input_x = torch.randint(1,10,(400,5)).type(torch.float32)
target = torch.randint(1,10,(400,1)).type(torch.float32)
# 02 线性层模型
model = nn.Linear(5,1)
# 03 优化器对象
sgd = optim.SGD(model.parameters(),lr=0.01) # 将模型参数w传入优化器
# 04 预测
y_pred = model(input_x)
# 05 损失函数
loss_fn = nn.MSELoss()
loss = loss_fn(y_pred,target) # 均方误差
# print(loss)
# 06 梯度清零
sgd.zero_grad() # 相当于 w.grad.zero_()
# 07 反向传播
loss.backward() # 1 损失函数对参数的导数 2 求梯度
# 08 梯度更新
sgd.step()
# 09 访问更新后的w
print(model.weight)
2、数据加载器
1.构建数据类
在 PyTorch 中,构建自定义数据加载类通常需要继承 torch.utils.data.Dataset 并实现以下几个方法:
1、init 方法 用于初始化数据集对象:通常在这里加载数据,或者定义如何从存储中获取数据的路径和方法。
python
def __init__(self,data,target):
self.data = data
self.target = target
2、len 方法 返回样本数量:需要实现,以便 Dataloader加载器能够知道数据集的大小。
python
def __len__(self):
return len(self.data)
3、getitem 方法 根据索引返回样本:将从数据集中提取一个样本,并可能对样本进行预处理或变换。如果需要进行更多的预处理或数据变换,可以在该方法中添加额外的逻辑。
python
def __getitem__(self,index):
return self.data[index],self.target[index]
2.数据加载器
在训练或者验证的时候,需要用到数据加载器批量的加载样本。
和构建数据整合:
python
import torch
import torch.nn as nn
from torch.utils.data import Dataset,DataLoader
class CustomDataSet(Dataset):
def __init__(self,data,target):
self.data = data
self.target = target
def __getitem__(self,index):
return self.data[index],self.target[index]
def __len__(self):
return len(self.data)
if __name__ == '__main__':
# data = CustomDataSet() -> __init__
# len(data) -> __len__
# data[0] -> __getitem__
x = torch.randn(100,3)
y = torch.randn(100,1)
# 数据集
data = CustomDataSet(x,y)
count = len(data)
print(count)
print(data[0])
"""
数据加载器
"""
data_loader = DataLoader(data,batch_size=16,shuffle=True) # 小批次设置为16 shuffle=True打乱顺序
for x,y in data_loader:
print(x.shape,y.shape)
3、数据集加载案例
1.加载Excel数据集
python
import torch
import pandas as pd
from torch.utils.data import Dataset,DataLoader
class my_excel_dataset(Dataset):
def __init__(self,path):
"""
读取excel文件, 提取特征值保存在data中, 标签值保存在target中
"""
data_pd = pd.read_excel(path)
# 数据处理:删除NAN的列
data_pd.dropna(axis=1,how='all')
# 将数据转换为DataFrame
data_pd = pd.DataFrame(data_pd)
# 删除原有的表头,并重命名列名为英文
data_pd.columns = ["zubie",
"student_id",
"name",
"self_work",
"expression",
"ppt",
"answer",
"present",
"defense",
"comments",]
data_pd = data_pd.drop(["zubie", "student_id", "name", "self_work", "comments"],axis=1)
# print(data_pd.head())
# 转换为tensor
self.data = torch.tensor(data_pd.iloc[:,:-1].to_numpy(),dtype=torch.float32)
self.target = torch.tensor(data_pd.iloc[:,-1].to_numpy(),dtype=torch.float32)
def __len__(self):
return len(self.data)
def __getitem__(self,index):
return self.data[index],self.target[index]
if __name__ == '__main__':
data = my_excel_dataset('./data/21级大数据答辩成绩表.xlsx')
data_loader = DataLoader(data,batch_size=4,shuffle=True)
for x,y in data_loader:
print(x,y)
2.加载图片数据集
os模块相关API:
python
import os
"""
os模块的API
"""
def test01():
"""
遍历当前目录
"""
for root, dirs, files in os.walk("./data"):
# root:当前目录路径 dirs:当前路径下所有子目录 files:当前路径下所有非目录子文件
print(root, dirs, files,'666')
def test02():
"""
查看文件
"""
path = os.path.join("./data", "1.png")
# 拼接路径 path = "./data"+"/"+"1.png"
print(path)
def test03():
"""
获取文件名
"""
_,str = os.path.split("./data/animal/cat")
print(str)
def test04():
"""
enumrate 生成枚举下标
"""
x=["20","hello","9999"]
for i in range(len(x)):
print(i,x[i])
x=["20","hello","9999"]
for i,el in enumerate(x):
print(i,el)
案例:
python
import os
import cv2
import torch
from torch.utils.data import Dataset,DataLoader
class my_image_dataset(Dataset):
def __init__(self,path):
self.path = path
self.classname=[]
self.data = []
self.label = []
# 遍历文件夹
for root,dirs,files in os.walk(path):
if root == path:
# 通过读取文件目录创建分类名
self.classname = dirs
# print(dirs)
# return
else:
for file in files:
# 单个文件的完整路径
file_path = os.path.join(root,file)
self.data.append(file_path)
# 该文件对应的类别
class_id = self.classname.index(os.path.split(root)[1])
self.label.append(class_id)
def __len__(self):
return len(self.data)
def __getitem__(self,index):
img_path = self.data[index] # x
label = self.label[index] # y
img = cv2.imread(img_path)
img = cv2.resize(img,(300,300))
img = torch.from_numpy(img)
# HWC to CHW
img = img.permute(2,0,1) # 维度转换
return img, label
if __name__ == '__main__':
data = my_image_dataset('./data/animal')
# print(data[120])
print(len(data))
print(data.classname)
train_loader = DataLoader(data, batch_size=32, shuffle=True)
for x,y in train_loader:
print(x) # [32,3,300,300] 单批次32张图片;3通道;300*300大小
print(y) # [32] 32个标签
# test04()
3.加载官方数据集
在 PyTorch 中官方提供了一些经典的数据集,如 CIFAR-10、MNIST、ImageNet 等,可以直接使用这些数据集进行训练和测试。
注意,该案例下需要联网下载官方数据集。官方地址:Datasets --- Torchvision 0.20 documentation
python
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
def test01():
transform = transforms.Compose([
transforms.ToTensor()
])
data = datasets.MNIST(
root='./data',
train=True,
download=True,
transform=transform
)
for x,y in DataLoader(data,batch_size=4,shuffle=True):
print(x.shape,y.shape)
if __name__ == '__main__':
test01()
4、数据增强
数据增强是提高模型泛化能力(鲁棒性)的一种有效方法,尤其在图像分类、目标检测等任务中。数据增强可以模拟更多的训练样本,从而减少过拟合风险。数据增强通过torchvision.transforms模块来实现。
具体参考官方文档:Illustration of transforms --- Torchvision 0.20 documentation