一、数据准备
1.1定义
数据准备(Data Preparation) 是数据分析与机器学习流程中的核心环节,指将原始数据转换为适合分析或建模的结构化格式的过程。
1.2组成
数据准备主要由两个部分组成,一个是划分数据集,一个是构建加载器。
二、构建数据类
2.1Dataset类
2.1.1基本介绍
Dataset是一个抽象类,自定义的数据类都要继承于它。它定义了数据集必须实现的方法。
这里必须的方法就有两个:
(1)__len__:获取数据集的大小;
(2)__getitem__ :通过索引访问数据集的样本;
还有一个初始化方法:
__init__:初始化数据,将需要加载的数据放在这里。
2.1.2三个方法的大致实现方式
(1)__init__:
def __init__(self,data,labels):self.data = dataself.labels = labels
注意:
左侧self.data是属性:存储在对象的内存空间中,生命周期与对象相同(只要对象存在就可用)。
右侧data是参数:它们是函数调用时传入的临时变量生命周期仅限于__init__方法执行期间。
(2)__len__:
def __len__(self):return len(self.data)
(3)__getitem__:
def __getitem__():sample = self.data[index]label = self.labels[index]return sample,label
注意:返回的是一个元组
2.1.3完整定义数据类
步骤:
(1)自定义类继承基类Dataset;
(2)实现len,getitem方法;
(3)通过__init__加载外部数据。
(4)创建函数,打印查看结果。
代码示例:
import torch
from torch import optim
import torch.nn as nn
from torch.utils.data import Dataset
class mydataset(Dataset):#继承def __init__(self,data,labels):self.data = dataself.labels = labelsdef __len__(self):return len(self.data)def __getitem__(self, index):index = min(max(index,0),len(self.data)-1)sample = self.data[index]label = self.labels[index]return sample,labeldef test():x = torch.randn(20,1,dtype=torch.float,requires_grad=True)y = torch.randn(20,1,dtype=torch.float)dataset = mydataset(x,y)print(dataset[0])model = nn.Linear(1,1)criterion = nn.MSELoss()optimizer = optim.Adam(model.parameters(),lr = 0.01)for epoch in range(20):y_pred = model(x)loss = criterion(y,y_pred)optimizer.zero_grad()loss.backward()optimizer.step()print(model.weight,model.bias)if __name__ == '__main__':test()
代码解释:
这段代码实现了一个简单的线性回归模型训练演示,主要作用是展示PyTorch的基本工作流程。具体来说:
-
创建随机数据:
-
生成20个随机数作为输入特征
x
-
生成20个随机数作为目标值
y
-
-
构建数据集:
-
使用自定义的
mydataset
类封装数据 -
实现数据集的长度和索引访问功能
-
-
定义线性模型:
-
创建最简单的神经网络:单层线性层 (
nn.Linear(1,1)
) -
相当于数学中的
y = wx + b
-
-
配置训练组件:
-
损失函数:均方误差 (
MSELoss
) -
优化器:Adam算法
-
-
执行训练循环:
-
重复20次训练过程:
-
模型预测 (
y_pred = model(x)
) -
计算预测值与真实值的差异 (
loss
) -
反向传播计算梯度 (
loss.backward()
) -
优化器更新参数 (
optimizer.step()
)
-
-
-
输出结果:
-
打印训练后的权重
w
和偏置b
-
结果:
(tensor([0.4495], grad_fn=<SelectBackward0>), tensor([-0.4830]))
Parameter containing:
tensor([[-0.3186]], requires_grad=True) Parameter containing:
tensor([0.7820], requires_grad=True)
2.2TensorDataset类
TensorDataset实行起来比Dataset自定义更简单些,它适用于数据已经是张量的形式。
直接调用TensorDataset就行
代码示例:
import torch
from torch.utils.data import TensorDataset
def tensordataset():features = torch.randn(12,3)labels = torch.randint(0,2,(12,))dataset = TensorDataset(features,labels)print(dataset[1])print(len(dataset))if __name__ == '__main__':# test()tensordataset()
结果:
(tensor([ 1.9409, -1.8119, 0.3687]), tensor(1))
12
三、数据加载器
3.1简单定义
数据加载器主要是用来加载数据集中的批量样本,有时候我们并不需要一股脑的把所有的数据样本都加载到模型中进行训练。
3.2数据加载器的作用
数据加载器是一个迭代器,要将里面的样本取出的话需要用到枚举法。
主要功能:
(1)批量加载数据;
(2)打乱数据;
(3)多线程加载。
3.3创建dataloder
dataloader = DataLoader(dataset,batch_size = 10,shuffle = True,num_workers = 2 #使用两个子进程加载数据
)
枚举:
enumerate返回枚举对象,生成由索引和值组成的元组。
-
enumerate(dataloader)
:-
dataloader
是一个 PyTorch 的 DataLoader 对象 -
enumerate()
函数会给每次迭代添加一个索引(从0开始)
-
-
batch_index
:-
当前批次的索引号(第几个批次)
-
从0开始计数
-
-
(samples, labels)
:-
解包 DataLoader 返回的一个批次的数据
-
samples
: 当前批次的输入数据(特征) -
labels
: 当前批次对应的标签
-
-
打印内容:
-
print(batch_index)
: 打印当前批次的序号 -
print(samples)
: 打印当前批次的所有样本数据 -
print(labels)
: 打印当前批次的所有标签
-
for batch_index,(samples,labels) in enumerate(dataloader):print(batch_index)print(samples)print(labels)
3.4相关案例
数据加载器一般是在数据准备后用,它不是放在类里面的,或者是放在循环中。
代码示例:
import torchfrom torch.utils.data import TensorDataset,DataLoader
def test():x = torch.randn(21,3,dtype=torch.float,requires_grad=True)y = torch.randn(21,1)dataset = TensorDataset(x,y)dataloader = DataLoader(dataset, batch_size=7, shuffle=True)for i, (batch_x, batch_y) in enumerate(dataloader):print(batch_x, batch_y)break
if __name__ == '__main__':test()
代码解释:
-
创建随机数据:
-
x
:21个样本,每个样本有3个特征(21×3的随机浮点数张量) -
y
:21个对应的标签(21×1的随机浮点数张量)
-
-
封装数据集:
-
使用
TensorDataset
将特征x
和标签y
组合成一个数据集
-
-
创建数据加载器:
-
使用
DataLoader
将数据集分成每批7个样本 -
shuffle=True
表示每次迭代前打乱数据顺序
-
-
分批获取数据:
-
遍历数据加载器获取批次数据
-
break
语句确保只打印第一个批次的数据
-
关键输出:
当运行代码时,会打印出第一个批次(7个样本)的数据:
batch_x (7×3张量): [[特征1, 特征2, 特征3],[特征1, 特征2, 特征3],...]batch_y (7×1张量): [标签1], [标签2], ...
结果:
tensor([[-1.0268, -0.8619, -1.6537],
[ 1.3707, -0.4312, 0.0943],
[ 0.3188, 0.9837, -0.5358],
[ 0.5535, 0.5231, -0.0356],
[ 1.3922, 0.7636, 0.3401],
[ 1.0909, 0.9076, 0.1129],
[ 0.8337, 0.7352, -1.0355]], grad_fn=<StackBackward0>) tensor([[ 0.6652],
[ 2.3144],
[ 1.6643],
[ 0.8918],
[-0.4568],
[ 0.9112],
[-0.5561]])
四、数据集加载案例
4.1加载csv数据集
注意文件路径的输入,可以输入绝对路径。
可以选择用自定义类的方式读取文件,也可以用TensorDataset读取文件,但是要注意后者所有数据必须全部转为张量。
(1)自定义类
import torch
from torch.utils.data import Dataset,DataLoader
import pandas as pd
class mycsv(Dataset):def __init__(self,filepath):df = pd.read_csv(filepath)df = df.drop(['学号','姓名'],axis=1)data = torch.tensor(df.values)self.data = data[:,:-1] #除了最后一列的所有行self.label = data[:,-1] #所有行的最后一列def __len__(self):return len(self.data)def __getitem__(self, idx):idx = min(max(idx,0),len(self.data)-1)return self.data[idx],self.label[idx]def test():csv_path = r'E:\AI\课件\深度学习\图片资料\大数据答辩成绩表.csv'dataset = mycsv(csv_path)print(dataset[0])dataloder = DataLoader(dataset,batch_size=3,shuffle=True)#批量加载3条数据内容for i,(data,label) in enumerate(dataloder):print(i,data,label)break
if __name__ == '__main__':test()
结果:
(tensor([16, 16, 21, 22]), tensor(75))
0 tensor([[15, 15, 20, 21],
[15, 16, 22, 22],
[16, 16, 21, 20]]) tensor([71, 75, 73])
(2)TensorDataset
'''
(1)读取数据
(2)数据处理
(3)转换张量
(4)TensorDataset
(5)返回
'''
def buid_dataset(filepath):df = pd.read_csv(filepath)df = df.drop(['学号','姓名'],axis=1)data = df.iloc[:,:-1]labels = df.iloc[:,-1]x = torch.tensor(data.values,dtype=torch.float)y = torch.tensor(labels.values)dataset = TensorDataset(x,y)return dataset'''
(1)读取文件路径
(2)数据集引入
(3)数据加载器
(4)枚举查看结果
'''
def test01():filepath = r'E:\AI\课件\深度学习\图片资料\大数据答辩成绩表.csv'dataset = buid_dataset(filepath)dataloader = DataLoader(dataset,batch_size=7,shuffle=True)for i,(data,label) in enumerate(dataloader):print(i,data,label)if __name__ == '__main__':# test()test01()
结果:
0 tensor([[15., 15., 21., 20.],
[16., 16., 25., 19.],
[18., 20., 29., 29.],
[18., 18., 25., 27.],
[15., 16., 23., 21.],
[17., 20., 27., 26.],
[18., 20., 27., 27.]]) tensor([71, 76, 96, 88, 75, 90, 92])
1 tensor([[19., 19., 27., 25.],
[15., 16., 21., 20.],
[18., 18., 22., 23.],
[16., 16., 21., 22.],
[14., 16., 22., 21.],
[14., 16., 22., 24.],
[17., 16., 21., 22.]]) tensor([90, 72, 81, 75, 73, 76, 76])
2 tensor([[15., 15., 20., 22.],
[15., 15., 20., 21.],
[15., 15., 20., 21.],
[16., 16., 23., 19.],
[18., 18., 24., 23.],
[18., 20., 28., 28.],
[19., 19., 28., 28.]]) tensor([72, 71, 71, 74, 83, 94, 94])
3 tensor([[14., 16., 22., 22.],
[19., 19., 28., 27.],
[15., 15., 21., 20.],
[15., 16., 23., 21.],
[17., 16., 20., 21.],
[17., 16., 24., 24.],
[15., 15., 21., 20.]]) tensor([74, 93, 71, 75, 74, 81, 71])
4 tensor([[17., 16., 21., 22.],
[18., 16., 20., 22.],
[18., 18., 25., 24.],
[15., 15., 20., 21.],
[15., 16., 22., 22.],
[14., 16., 22., 21.],
[18., 16., 20., 22.]]) tensor([76, 76, 85, 71, 75, 73, 76])
5 tensor([[15., 15., 21., 20.],
[15., 16., 23., 22.],
[19., 19., 28., 24.],
[15., 16., 23., 21.],
[16., 16., 23., 19.],
[15., 16., 21., 20.],
[17., 16., 22., 23.]]) tensor([71, 76, 90, 75, 74, 72, 78])
6 tensor([[15., 15., 20., 22.],
[16., 16., 21., 20.],
[16., 16., 21., 22.]]) tensor([72, 73, 75])
4.2加载图片数据集
这里主要介绍imageFolder构造函数的方法:
import torch
from torchvision import datasets, transforms
import os
from torch.utils.data import DataLoader
from matplotlib import pyplot as plttorch.manual_seed(42)def load():path = os.path.join(os.path.dirname(__file__), 'dataset')print(path)transform = transforms.Compose([transforms.Resize((112, 112)),transforms.ToTensor()])dataset = datasets.ImageFolder(path, transform=transform)dataloader = DataLoader(dataset, batch_size=1, shuffle=True)for x,y in dataloader:x = x.squeeze(0).permute(1, 2, 0).numpy()plt.imshow(x)plt.show()print(y[0])breakif __name__ == '__main__':load()
4.3加载官方数据集
pytorch框架中有一些经典的官方数据集,我们举例以cifar-10为例。
CIFAR10: 包含 10 个类别的 60,000 张 32x32 彩色图像,每个类别 6,000 张图像。 其中训练集50,000张,测试集10,000张。
下载官方数据集要引入torchvision中的transforms,datasets。
API:from torchvision import transforms,datasets
代码示例:
def test():transform = transforms.Compose([transforms.ToTensor(),])# 训练数据集data_train = datasets.CIFAR10(root="./data",train=True,download=True,transform=transform,)trainloader = DataLoader(data_train, batch_size=4, shuffle=True, num_workers=2)for x, y in trainloader:print(x.shape)print(y)break# 测试数据集data_test = datasets.CIFAR10(root="./data",train=False,download=True,transform=transform,)testloader = DataLoader(data_test, batch_size=4, shuffle=False, num_workers=2)for x, y in testloader:print(x.shape)print(y)breakif __name__ == '__main__':test()
代码解释:
-
datasets.CIFAR10
参数:root="./data"
:数据存储路径。train=True
:加载训练集(50,000 张图像)。download=True
:若数据不存在,则自动下载。transform=transform
:应用前面定义的预处理。
-
DataLoader
参数:batch_size=4
:每次加载 4 张图像和标签。shuffle=True
:每个 epoch 前打乱数据,增强训练随机性。num_workers=2
:使用 2 个子进程并行加载数据(提高效率)
这段代码的核心功能是:
- 数据加载:通过
torchvision.datasets
下载 CIFAR-10 数据集,并使用DataLoader
批量加载。 - 数据格式展示:
- 输入:
[batch_size, channels, height, width]
的张量。 - 标签:类别索引(0-9)的张量。
- 输入:
- 验证数据结构:通过打印第一批数据,确认数据格式是否符合预期。
五、激活函数
5.1基本概念
如果在隐层不使用激活函数,神经网络表现为线性模型,整个网络就是线性模型,无法捕捉数据中的非线性关系。 这时候做回归比较好,做分类的效果不太好。
非线性可视化:A Neural Network Playground
该网址可以查看激活函数对分类和回归的效果,更直观观察结果。
激活函数的作用是在隐藏层引入非线性,使得神经网络能够学习和表示复杂的函数关系,使网络具备非线性能力,增强其表达能力。
5.2分类
5.2.1sigmoid函数
sigmoid函数适合运用于二分类问题中,它将神经网络的输出值处理后,将值压缩到0-1之间,适合处理概率问题。
缺点:
(1)出现梯度消失的情况;
(2)信息丢失:输入100和输入10000,他们的结果都是映射到0-1之间;
(3)计算量大,设计指数运算。
sigmoid的计算公式:
5.2.2tanh函数
tanh(双曲正切)也是常见的激活函数,用于处理分类问题,它的输出范围为(-1,1),当输入为0时,它的输出也为0。
缺点:
(1)在sigmoid的基础上改进了些,但也涉及梯度消失问题;
(2)计算成本大,也设计指数运算。
计算公式:
5.2.3ReLU函数
ReLU(修正线性单元),它的计算简单,缓解了梯度消失问题,因为ReLU的正半求导区恒为1,梯度不会变小,负半区输入小于等于0,这会导致部分神经元死亡,从而降低模型的表达能力,但是也有好处,稀疏激活,这样可以减少冗余信息。
计算公式:
5.2.4LeakyReLU函数
LeakyLeLU是ReLU的改进版本,主要是为了解决ReLU的神经元死亡问题。它的计算还是比较简单快捷的。
缺点:
(1)参数选择,α是一超参数,选择一个合适的α值需要反复测试;
(2)参数设置不对可能会导致激活值过低。
计算公式:
注意:ReLU(Rectified Linear Unit)和LeakyReLU(Leaky Rectified Linear Unit)既可用于线性回归,也可用于分类任务,它们本身不是特定于某种任务,而是神经网络中的激活函数。
5.2.5softmax函数
softmax函数是用于分类问题的输出层,将输出结果转换为概率分布,使输出的各个类别的概率之和为1。softmax只是对向量的值做了改变,但它的位置不变。
特点:
(1)将输出值转换为0-1之间的概率分布;
(2)概率最大的类别更接近1,其他接近于0;
(3)运用于分类。
计算公式:
5.3合适的选择
激活函数的应用,主要是应用在隐藏层或者输出层。
隐藏层:优先选择ReLU,然后是LeakyReLU,避免sigmoid,尝试tanh
输出层:
(1)二分类:sigmoid
(2)多分类:softmax
六、小结
主要学习了数据准备的构建数据类和数据加载器,主要是熟练运用操作。其次是熟悉激活函数,记住特征。