PyTorch实现前馈神经网络(手动)

回归任务

该回归问题为高维回归问题,涉及的自变量过多,因此不适合用之前的线性回归模型,因为模型过于简单容易导致训练的效率太低。因此我们考虑利用前馈神经网络,增加隐藏层,并且为了避免梯度消失问题,在隐藏层采用relu激活函数。

导入包

import torch
import numpy as np
import random
from IPython import display
import torch.utils.data as Data

自定义数据集

num_inputs = 500
num_examples = 10000
#
true_w = torch.ones(500,1)*0.0056
true_b = 0.028
#随机生成的数据样本
features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype=torch.float)#行*列=10000*500
labels = torch.mm(features,true_w) + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float) #扰动项
#训练集和测试集上的样本&标签数----真实的特征和样本
trainfeatures = features[:7000]
trainlabels = labels[:7000]
testfeatures = features[7000:]  
testlabels = labels[7000:]
print(trainfeatures.shape,trainlabels.shape,testfeatures.shape,testlabels.shape)
torch.Size([7000, 500]) torch.Size([7000, 1]) torch.Size([3000, 500]) torch.Size([3000, 1])

构造数据迭代器

采用torch.utils.data.DataLoader读取小批量数据,分别定义关于训练集数据和测试集数据的迭代器iterate

  • batch_size是超参数,表示一轮放入多少个样本进行训练
  • shuffle是否打乱数据,True表示打乱数据
  • num_workers=0表示不开启多线程读取数据

注:利用Data.TensorDataset获取数据集,Data.DataLoader构建数据迭代器,从而实现数据的批量读取。

#获得数据迭代器
batch_size = 50 # 设置小批量大小
def load_array(data_arrays, batch_size, is_train=True):  #自定义函数
    """构造一个PyTorch数据迭代器。"""
    dataset = Data.TensorDataset(*data_arrays)#features 和 labels作为list传入,得到PyTorch的一个数据集
    return Data.DataLoader(dataset, batch_size, shuffle=is_train,num_workers=0)#返回的是实例化后的DataLoader
train_iter = load_array([trainfeatures,trainlabels],batch_size)
test_iter = load_array([testfeatures,testlabels],batch_size)
# 测试:利用python内置函数next,从迭代器中获取第一项。
next(iter(train_iter))

初始化参数

初始化参数针对神经网络相关数据:

  • 宏观层面(超参数):输入层、隐藏层、输出层的维度,分别为:num_inputs/num_hiddens/num_outputs;
  • 微观层面:W1,b1,W2,b2

注:如果从A–>B,则W.shape = (A * B),这样可以直接参与mm,不用转置.t()

由于XW1 + b1结构:X.shape=(num_example * num_input) 因此W1.shape=(num_inputs * num_hiddens)
XW1结果为:num_example * num_hiddens作为隐藏层的输入

#定义超参数
num_inputs=500
num_hiddens = 256
num_outputs = 1
#定义参数
W1 = torch.tensor(np.random.normal(0, 0.01, (num_inputs,num_hiddens)), dtype=torch.float32)  
b1 = torch.zeros(1, dtype=torch.float32)  
W2 = torch.tensor(np.random.normal(0, 0.01, (num_hiddens,num_outputs)), dtype=torch.float32)  
b2 = torch.zeros(1, dtype=torch.float32)  
params = [W1,b1,W2,b2]
for param in params:
    param.requires_grad_(requires_grad = True)#设置为true,追踪并记录所有在计算图上的操作(正向积累)

定义隐藏层激活函数

对隐藏层通常使用Relu激活函数,当x>0时,不存在梯度消失问题

def relu(x):
    x=torch.max(x,torch.tensor(0.0))
    return x
#     a = torch.zeros_like(x)#与X.shape一致的零张量
#     return torch.max(x,a)#即X>0.输出为X,否则输出为0
x =torch.tensor(2)
relu(x),relu(-x)

定义模型

#定义模型  
def net(X):  
    X = X.view((-1,num_inputs)) #将数据进行展平,对于空间结构的数据生效
    H = relu(torch.matmul(X,W1)+b1)  
    return torch.matmul(H,W2)+b2  
#测试 @可以表示矩阵乘法
a=torch.ones(2,3)
b = torch.ones(3,4)
a@b,(a@b).size()

损失函数

由于是回归问题,因此使用torch模块自带的最小化均方误差。对于回归问题,损失是唯一的衡量标准,对于分类问题,一般采取更直观的角度进行衡量模型的效果,即分类的准确率。

loss = torch.nn.MSELoss()

优化算法

小批量随机梯度下降:

在每一步中,使用从数据集中随机抽取的一个小批量,然后根据参数计算损失的梯度。接下来,朝着减少损失的方向更新我们的参数。 下面的函数实现小批量随机梯度下降更新。该函数接受模型参数集合、学习速率和批量大小作为输入。每一步更新的大小由学习速率lr决定。

理解下面这句话:
因为我们计算的损失是一个批量样本的总和,所以我们用批量大小(batch_size)来归一化步长,这样步长大小就不会取决于我们对批量大小的选择。(步长即∆param.data,param.grad与批量样本的大小有关,因此需要归一化)

def SGD (params,lr,batch_size):
    for param in params:
        param.data -= lr * param.grad/batch_size

训练

定义训练函数

对于每一轮次的训练;

  • step1:在训练集上,进行小批量梯度下降更新参数
  • step2 每经过一个轮次的训练, 记录训练集和测试集上的loss
#记录列表(list),存储训练集和测试集上经过每一轮次,loss的变化
def train 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江鸟阁长

你的支持是我更新的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值